Skip to content

Commit d6843c3

Browse files
committed
signIn and signOut pages moved to proxy
template pkg add static_files_test.go file fix TestSecurityHeaders test fix TestOAuthStart: replace template interface fix TestHTTPSRedirect partial but incomplete fix for test_provider GetSignInURL/GetSignOutURL add initial TestSignOutPage function fix sign out with no session workflow some tidyings sso_auth:additional redirect URI changes sso_auth: authenticagtor test changes sso_proxy: oauthproxy tests
1 parent 7edfee7 commit d6843c3

File tree

13 files changed

+773
-369
lines changed

13 files changed

+773
-369
lines changed

internal/auth/authenticator.go

Lines changed: 32 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -134,36 +134,6 @@ type signInResp struct {
134134
Version string
135135
}
136136

137-
// SignInPage directs the user to the sign in page
138-
func (p *Authenticator) SignInPage(rw http.ResponseWriter, req *http.Request, code int) {
139-
rw.WriteHeader(code)
140-
141-
// We construct this URL based on the known callback URL that we send to Google.
142-
// We don't want to rely on req.Host, as that can be attacked via Host header injection
143-
// This ends up looking like:
144-
// https://sso-auth.example.com/sign_in?client_id=...&redirect_uri=...
145-
path := strings.TrimPrefix(req.URL.Path, "/")
146-
redirectURL := p.redirectURL.ResolveReference(
147-
&url.URL{
148-
Path: path,
149-
RawQuery: req.URL.RawQuery,
150-
},
151-
)
152-
153-
// validateRedirectURI middleware already ensures that this is a valid URL
154-
destinationURL, _ := url.Parse(redirectURL.Query().Get("redirect_uri"))
155-
156-
t := signInResp{
157-
ProviderName: p.provider.Data().ProviderName,
158-
ProviderSlug: p.provider.Data().ProviderSlug,
159-
EmailDomains: p.EmailDomains,
160-
Redirect: redirectURL.String(),
161-
Destination: destinationURL.Host,
162-
Version: VERSION,
163-
}
164-
p.templates.ExecuteTemplate(rw, "sign_in.html", t)
165-
}
166-
167137
func (p *Authenticator) authenticate(rw http.ResponseWriter, req *http.Request) (*sessions.SessionState, error) {
168138
logger := log.NewLogEntry()
169139
remoteAddr := getRemoteAddr(req)
@@ -257,20 +227,36 @@ func (p *Authenticator) SignIn(rw http.ResponseWriter, req *http.Request) {
257227
"action:sign_in",
258228
fmt.Sprintf("proxy_host:%s", proxyHost),
259229
}
230+
231+
// We construct this URL based on the known callback URL that we send to Google.
232+
// We don't want to rely on req.Host, as that can be attacked via Host header injection
233+
// This ends up looking like:
234+
// https://sso-auth.example.com/sign_in?client_id=...&redirect_uri=...
235+
//
236+
// The validateRedirectURI middleware ensures that this is a valid URL
237+
path := strings.TrimPrefix(req.URL.Path, "/")
238+
redirectURL := p.redirectURL.ResolveReference(
239+
&url.URL{
240+
Path: path,
241+
RawQuery: req.URL.RawQuery,
242+
},
243+
)
244+
req.URL = redirectURL
245+
260246
session, err := p.authenticate(rw, req)
261247
switch err {
262248
case nil:
263249
// User is authenticated, redirect back to the proxy application
264250
// with the necessary state
265251
p.ProxyOAuthRedirect(rw, req, session, tags)
266252
case http.ErrNoCookie:
267-
p.SignInPage(rw, req, http.StatusOK)
253+
p.OAuthStart(rw, req)
268254
case providers.ErrTokenRevoked:
269255
p.sessionStore.ClearSession(rw, req)
270-
p.SignInPage(rw, req, http.StatusOK)
256+
p.OAuthStart(rw, req)
271257
case sessions.ErrLifetimeExpired, sessions.ErrInvalidSession:
272258
p.sessionStore.ClearSession(rw, req)
273-
p.SignInPage(rw, req, http.StatusOK)
259+
p.OAuthStart(rw, req)
274260
default:
275261
tags = append(tags, "error:sign_in_error")
276262
p.StatsdClient.Incr("application_error", tags, 1.0)
@@ -373,17 +359,13 @@ func (p *Authenticator) SignOut(rw http.ResponseWriter, req *http.Request) {
373359
fmt.Sprintf("proxy_host:%s", proxyHost),
374360
}
375361

376-
if req.Method == "GET" {
377-
p.SignOutPage(rw, req, "")
378-
return
379-
}
380-
381362
session, err := p.sessionStore.LoadSession(req)
382363
switch err {
383364
case nil:
365+
// no error - we were able to load the session. continue onwards.
384366
break
385-
// if there's no cookie in the session we can just redirect
386367
case http.ErrNoCookie:
368+
// if there's no session, we can just redirect back.
387369
http.Redirect(rw, req, redirectURI, http.StatusFound)
388370
return
389371
default:
@@ -399,95 +381,49 @@ func (p *Authenticator) SignOut(rw http.ResponseWriter, req *http.Request) {
399381
tags = append(tags, "error:revoke_session")
400382
p.StatsdClient.Incr("provider_error", tags, 1.0)
401383
logger.Error(err, "error revoking session")
402-
p.SignOutPage(rw, req, "An error occurred during sign out. Please try again.")
384+
//TODO: This used to return a sign out page with an error.
385+
//TODO: http.StatusInternalServerError or codeForError(err)
386+
p.ErrorResponse(rw, req, err.Error(), http.StatusInternalServerError)
403387
return
404388
}
405389

390+
// if we reach here, the session has been revoked on the identity providers end,
391+
// clear our session and redirect.
406392
p.sessionStore.ClearSession(rw, req)
407393
http.Redirect(rw, req, redirectURI, http.StatusFound)
408394
}
409395

410-
type signOutResp struct {
411-
ProviderSlug string
412-
Version string
413-
Redirect string
414-
Signature string
415-
Timestamp string
416-
Message string
417-
Destination string
418-
Email string
419-
}
420-
421-
// SignOutPage renders a sign out page with a message
422-
func (p *Authenticator) SignOutPage(rw http.ResponseWriter, req *http.Request, message string) {
423-
// validateRedirectURI middleware already ensures that this is a valid URL
424-
redirectURI := req.Form.Get("redirect_uri")
425-
426-
session, err := p.sessionStore.LoadSession(req)
427-
if err != nil {
428-
http.Redirect(rw, req, redirectURI, http.StatusFound)
429-
return
430-
}
431-
432-
signature := req.Form.Get("sig")
433-
timestamp := req.Form.Get("ts")
434-
destinationURL, _ := url.Parse(redirectURI)
435-
436-
// An error message indicates that an internal server error occurred
437-
if message != "" {
438-
rw.WriteHeader(http.StatusInternalServerError)
439-
}
440-
441-
t := signOutResp{
442-
ProviderSlug: p.provider.Data().ProviderSlug,
443-
Version: VERSION,
444-
Redirect: redirectURI,
445-
Signature: signature,
446-
Timestamp: timestamp,
447-
Message: message,
448-
Destination: destinationURL.Host,
449-
Email: session.Email,
450-
}
451-
p.templates.ExecuteTemplate(rw, "sign_out.html", t)
452-
return
453-
}
454-
455396
// OAuthStart starts the authentication process by redirecting to the provider. It provides a
456397
// `redirectURI`, allowing the provider to redirect back to the sso proxy after authentication.
457398
func (p *Authenticator) OAuthStart(rw http.ResponseWriter, req *http.Request) {
458399
tags := []string{"action:start"}
459400

460401
nonce := fmt.Sprintf("%x", aead.GenerateKey())
461402
p.csrfStore.SetCSRF(rw, req, nonce)
462-
authRedirectURL, err := url.Parse(req.URL.Query().Get("redirect_uri"))
463-
if err != nil || !validRedirectURI(authRedirectURL.String(), p.ProxyRootDomains) {
464-
tags = append(tags, "error:invalid_redirect_parameter")
465-
p.StatsdClient.Incr("application_error", tags, 1.0)
466-
p.ErrorResponse(rw, req, "Invalid redirect parameter", http.StatusBadRequest)
467-
return
468-
}
403+
469404
// Here we validate the redirect that is nested within the redirect_uri.
470405
// `authRedirectURL` points to step D, `proxyRedirectURL` points to step E.
471406
//
472407
// A* B C D E
473408
// /start -> Google -> auth /callback -> /sign_in -> proxy /callback
474409
//
475410
// * you are here
476-
proxyRedirectURL, err := url.Parse(authRedirectURL.Query().Get("redirect_uri"))
411+
412+
proxyRedirectURL, err := url.Parse(req.URL.Query().Get("redirect_uri"))
477413
if err != nil || !validRedirectURI(proxyRedirectURL.String(), p.ProxyRootDomains) {
478414
tags = append(tags, "error:invalid_redirect_parameter")
479415
p.StatsdClient.Incr("application_error", tags, 1.0)
480416
p.ErrorResponse(rw, req, "Invalid redirect parameter", http.StatusBadRequest)
481417
return
482418
}
483-
proxyRedirectSig := authRedirectURL.Query().Get("sig")
484-
ts := authRedirectURL.Query().Get("ts")
419+
proxyRedirectSig := req.URL.Query().Get("sig")
420+
ts := req.URL.Query().Get("ts")
485421
if !validSignature(proxyRedirectURL.String(), proxyRedirectSig, ts, p.ProxyClientSecret) {
486422
p.ErrorResponse(rw, req, "Invalid redirect parameter", http.StatusBadRequest)
487423
return
488424
}
489425
redirectURI := p.GetRedirectURI(req.Host)
490-
state := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", nonce, authRedirectURL.String())))
426+
state := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", nonce, req.URL.String())))
491427
signInURL := p.provider.GetSignInURL(redirectURI, state)
492428
http.Redirect(rw, req, signInURL, http.StatusFound)
493429
}

0 commit comments

Comments
 (0)