Skip to content

Commit

Permalink
Add /auth endpoint to support Nginx's auth_request
Browse files Browse the repository at this point in the history
Closes bitly#152.
  • Loading branch information
Mike Bland authored and Ruta Sakalauskaite committed Jan 21, 2016
1 parent d89ed08 commit 4b4a69c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ OAuth2 Proxy responds directly to the following endpoints. All other endpoints w
* /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)
* /oauth2/start - a URL that will redirect to start the OAuth cycle
* /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
* /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)

## Logging Format

Expand Down
19 changes: 19 additions & 0 deletions oauthproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type OAuthProxy struct {
SignInPath string
OAuthStartPath string
OAuthCallbackPath string
AuthOnlyPath string

redirectURL *url.URL // the url to receive requests at
provider providers.Provider
Expand Down Expand Up @@ -156,6 +157,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyPrefix),
OAuthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix),
OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),

ProxyPrefix: opts.ProxyPrefix,
provider: opts.provider,
Expand Down Expand Up @@ -390,6 +392,8 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
p.OAuthStart(rw, req)
case path == p.OAuthCallbackPath:
p.OAuthCallback(rw, req)
case path == p.AuthOnlyPath:
p.AuthenticateOnly(rw, req)
default:
p.Proxy(rw, req)
}
Expand Down Expand Up @@ -465,6 +469,21 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
}
}

func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
remoteAddr := getRemoteAddr(req)
if session, _, err := p.LoadCookiedSession(req); err != nil {
log.Printf("%s %s", remoteAddr, err)
} else if session.IsExpired() {
log.Printf("%s Expired", remoteAddr, session)
} else if !p.Validator(session.Email) {
log.Printf("%s Permission Denied", remoteAddr, session)
} else {
rw.WriteHeader(http.StatusAccepted)
return
}
http.Error(rw, "unauthorized request", http.StatusUnauthorized)
}

func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
var saveSession, clearSession, revalidated bool
remoteAddr := getRemoteAddr(req)
Expand Down
55 changes: 55 additions & 0 deletions oauthproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,58 @@ func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
t.Errorf("expected nil session %#v", session)
}
}

func NewAuthOnlyEndpointTest() *ProcessCookieTest {
pc_test := NewProcessCookieTestWithDefaults()
pc_test.req, _ = http.NewRequest("GET",
pc_test.opts.ProxyPrefix + "/auth", nil)
return pc_test
}

func TestAuthOnlyEndpointAccepted(t *testing.T) {
test := NewAuthOnlyEndpointTest()
startSession := &providers.SessionState{
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
test.SaveSession(startSession, time.Now())

test.proxy.ServeHTTP(test.rw, test.req)
assert.Equal(t, http.StatusAccepted, test.rw.Code)
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
assert.Equal(t, "", string(bodyBytes))
}

func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
test := NewAuthOnlyEndpointTest()

test.proxy.ServeHTTP(test.rw, test.req)
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
}

func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
test := NewAuthOnlyEndpointTest()
test.proxy.CookieExpire = time.Duration(24) * time.Hour
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
startSession := &providers.SessionState{
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
test.SaveSession(startSession, reference)

test.proxy.ServeHTTP(test.rw, test.req)
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
}

func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
test := NewAuthOnlyEndpointTest()
startSession := &providers.SessionState{
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
test.SaveSession(startSession, time.Now())
test.validate_user = false

test.proxy.ServeHTTP(test.rw, test.req)
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
}

0 comments on commit 4b4a69c

Please sign in to comment.