Added userinfo endpoint (#300)
* Added userinfo endpoint * Added documentation for the userinfo endpoint * Update oauthproxy.go Co-Authored-By: Dan Bond <pm@danbond.io> * Suggested fixes : Streaming json to rw , header set after error check * Update oauthproxy.go Co-Authored-By: Dan Bond <pm@danbond.io> * fix session.Email * Ported tests and updated changelog
This commit is contained in:
		
							parent
							
								
									2a07983a36
								
							
						
					
					
						commit
						fef940da9a
					
				|  | @ -11,6 +11,7 @@ | |||
| - [#286](https://github.com/pusher/oauth2_proxy/pull/286) Requests.go updated with useful error messages (@biotom) | ||||
| - [#302](https://github.com/pusher/oauth2_proxy/pull/302) Rewrite dist script (@syscll) | ||||
| - [#304](https://github.com/pusher/oauth2_proxy/pull/304) Add new Logo! :tada: (@JoelSpeed) | ||||
| - [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze) | ||||
| 
 | ||||
| # v4.0.0 | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,4 +14,5 @@ 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/userinfo - the URL is used to return user's email from the session in JSON format. | ||||
| - /oauth2/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](#nginx-auth-request) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	b64 "encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html/template" | ||||
|  | @ -75,6 +76,7 @@ type OAuthProxy struct { | |||
| 	OAuthStartPath    string | ||||
| 	OAuthCallbackPath string | ||||
| 	AuthOnlyPath      string | ||||
| 	UserInfoPath      string | ||||
| 
 | ||||
| 	redirectURL         *url.URL // the url to receive requests at
 | ||||
| 	whitelistDomains    []string | ||||
|  | @ -266,6 +268,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { | |||
| 		OAuthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | ||||
| 		OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | ||||
| 		AuthOnlyPath:      fmt.Sprintf("%s/auth", opts.ProxyPrefix), | ||||
| 		UserInfoPath:      fmt.Sprintf("%s/userinfo", opts.ProxyPrefix), | ||||
| 
 | ||||
| 		ProxyPrefix:         opts.ProxyPrefix, | ||||
| 		provider:            opts.provider, | ||||
|  | @ -557,6 +560,8 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | |||
| 		p.OAuthCallback(rw, req) | ||||
| 	case path == p.AuthOnlyPath: | ||||
| 		p.AuthenticateOnly(rw, req) | ||||
| 	case path == p.UserInfoPath: | ||||
| 		p.UserInfo(rw, req) | ||||
| 	default: | ||||
| 		p.Proxy(rw, req) | ||||
| 	} | ||||
|  | @ -585,6 +590,22 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| //UserInfo endpoint outputs session email in JSON format
 | ||||
| func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) { | ||||
| 
 | ||||
| 	session, err := p.getAuthenticatedSession(rw, req) | ||||
| 	if err != nil { | ||||
| 		http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) | ||||
| 		return | ||||
| 	} | ||||
| 	userInfo := struct { | ||||
| 		Email string `json:"email"` | ||||
| 	}{session.Email} | ||||
| 	rw.Header().Set("Content-Type", "application/json") | ||||
| 	rw.WriteHeader(http.StatusOK) | ||||
| 	json.NewEncoder(rw).Encode(userInfo) | ||||
| } | ||||
| 
 | ||||
| // SignOut sends a response to clear the authentication cookie
 | ||||
| func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { | ||||
| 	p.ClearSessionCookie(rw, req) | ||||
|  |  | |||
|  | @ -746,6 +746,32 @@ func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func NewUserInfoEndpointTest() *ProcessCookieTest { | ||||
| 	pcTest := NewProcessCookieTestWithDefaults() | ||||
| 	pcTest.req, _ = http.NewRequest("GET", | ||||
| 		pcTest.opts.ProxyPrefix+"/userinfo", nil) | ||||
| 	return pcTest | ||||
| } | ||||
| 
 | ||||
| func TestUserInfoEndpointAccepted(t *testing.T) { | ||||
| 	test := NewUserInfoEndpointTest() | ||||
| 	startSession := &sessions.SessionState{ | ||||
| 		Email: "john.doe@example.com", AccessToken: "my_access_token"} | ||||
| 	test.SaveSession(startSession) | ||||
| 
 | ||||
| 	test.proxy.ServeHTTP(test.rw, test.req) | ||||
| 	assert.Equal(t, http.StatusOK, test.rw.Code) | ||||
| 	bodyBytes, _ := ioutil.ReadAll(test.rw.Body) | ||||
| 	assert.Equal(t, "{\"email\":\"john.doe@example.com\"}\n", string(bodyBytes)) | ||||
| } | ||||
| 
 | ||||
| func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) { | ||||
| 	test := NewUserInfoEndpointTest() | ||||
| 
 | ||||
| 	test.proxy.ServeHTTP(test.rw, test.req) | ||||
| 	assert.Equal(t, http.StatusUnauthorized, test.rw.Code) | ||||
| } | ||||
| 
 | ||||
| func NewAuthOnlyEndpointTest(modifiers ...OptionsModifier) *ProcessCookieTest { | ||||
| 	pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...) | ||||
| 	pcTest.req, _ = http.NewRequest("GET", | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue