git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add /auth endpoint to support Nginx's auth_request

Closes #152.

commite61fc9e7a67c85c97516ba6804cd4e0e45bc2a8c authorMike Bland <michael.bland@gsa.gov> dateThu 8 Oct 2015, 09:27 (11 years ago) parente6e2dbe
3 files changed+75
README.md+1 oauthproxy.go+19 oauthproxy_test.go+55
README.md+1
@@ -249,6 +249,7 @@ OAuth2 Proxy responds directly to the following endpoints. All other endpoints w
249249* /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)
250250* /oauth2/start - a URL that will redirect to start the OAuth cycle
251251* /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
252+* /oauth2/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html)
252253
253254## Logging Format
254255
oauthproxy.go+19
@@ -33,6 +33,7 @@ type OAuthProxy struct {
3333 SignInPath string
3434 OAuthStartPath string
3535 OAuthCallbackPath string
36+ AuthOnlyPath string
3637
3738 redirectURL *url.URL // the url to receive requests at
3839 provider providers.Provider
@@ -156,6 +157,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
156157 SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyPrefix),
157158 OAuthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix),
158159 OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
160+ AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
159161
160162 ProxyPrefix: opts.ProxyPrefix,
161163 provider: opts.provider,
@@ -390,6 +392,8 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
390392 p.OAuthStart(rw, req)
391393 case path == p.OAuthCallbackPath:
392394 p.OAuthCallback(rw, req)
395+ case path == p.AuthOnlyPath:
396+ p.AuthenticateOnly(rw, req)
393397 default:
394398 p.Proxy(rw, req)
395399 }
@@ -465,6 +469,21 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
465469 }
466470}
467471
472+func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
473+ remoteAddr := getRemoteAddr(req)
474+ if session, _, err := p.LoadCookiedSession(req); err != nil {
475+ log.Printf("%s %s", remoteAddr, err)
476+ } else if session.IsExpired() {
477+ log.Printf("%s Expired", remoteAddr, session)
478+ } else if !p.Validator(session.Email) {
479+ log.Printf("%s Permission Denied", remoteAddr, session)
480+ } else {
481+ rw.WriteHeader(http.StatusAccepted)
482+ return
483+ }
484+ http.Error(rw, "unauthorized request", http.StatusUnauthorized)
485+}
486+
468487func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
469488 var saveSession, clearSession, revalidated bool
470489 remoteAddr := getRemoteAddr(req)
oauthproxy_test.go+55
@@ -555,3 +555,58 @@ func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
555555 t.Errorf("expected nil session %#v", session)
556556 }
557557}
558+
559+func NewAuthOnlyEndpointTest() *ProcessCookieTest {
560+ pc_test := NewProcessCookieTestWithDefaults()
561+ pc_test.req, _ = http.NewRequest("GET",
562+ pc_test.opts.ProxyPrefix + "/auth", nil)
563+ return pc_test
564+}
565+
566+func TestAuthOnlyEndpointAccepted(t *testing.T) {
567+ test := NewAuthOnlyEndpointTest()
568+ startSession := &providers.SessionState{
569+ Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
570+ test.SaveSession(startSession, time.Now())
571+
572+ test.proxy.ServeHTTP(test.rw, test.req)
573+ assert.Equal(t, http.StatusAccepted, test.rw.Code)
574+ bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
575+ assert.Equal(t, "", string(bodyBytes))
576+}
577+
578+func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
579+ test := NewAuthOnlyEndpointTest()
580+
581+ test.proxy.ServeHTTP(test.rw, test.req)
582+ assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
583+ bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
584+ assert.Equal(t, "unauthorized request\n", string(bodyBytes))
585+}
586+
587+func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
588+ test := NewAuthOnlyEndpointTest()
589+ test.proxy.CookieExpire = time.Duration(24) * time.Hour
590+ reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
591+ startSession := &providers.SessionState{
592+ Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
593+ test.SaveSession(startSession, reference)
594+
595+ test.proxy.ServeHTTP(test.rw, test.req)
596+ assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
597+ bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
598+ assert.Equal(t, "unauthorized request\n", string(bodyBytes))
599+}
600+
601+func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
602+ test := NewAuthOnlyEndpointTest()
603+ startSession := &providers.SessionState{
604+ Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
605+ test.SaveSession(startSession, time.Now())
606+ test.validate_user = false
607+
608+ test.proxy.ServeHTTP(test.rw, test.req)
609+ assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
610+ bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
611+ assert.Equal(t, "unauthorized request\n", string(bodyBytes))
612+}