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) | - [#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) | - [#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) | - [#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 | # 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/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/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/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) | - /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" | 	"context" | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
| 	b64 "encoding/base64" | 	b64 "encoding/base64" | ||||||
|  | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"html/template" | 	"html/template" | ||||||
|  | @ -75,6 +76,7 @@ type OAuthProxy struct { | ||||||
| 	OAuthStartPath    string | 	OAuthStartPath    string | ||||||
| 	OAuthCallbackPath string | 	OAuthCallbackPath string | ||||||
| 	AuthOnlyPath      string | 	AuthOnlyPath      string | ||||||
|  | 	UserInfoPath      string | ||||||
| 
 | 
 | ||||||
| 	redirectURL         *url.URL // the url to receive requests at
 | 	redirectURL         *url.URL // the url to receive requests at
 | ||||||
| 	whitelistDomains    []string | 	whitelistDomains    []string | ||||||
|  | @ -266,6 +268,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy { | ||||||
| 		OAuthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | 		OAuthStartPath:    fmt.Sprintf("%s/start", opts.ProxyPrefix), | ||||||
| 		OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | 		OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix), | ||||||
| 		AuthOnlyPath:      fmt.Sprintf("%s/auth", opts.ProxyPrefix), | 		AuthOnlyPath:      fmt.Sprintf("%s/auth", opts.ProxyPrefix), | ||||||
|  | 		UserInfoPath:      fmt.Sprintf("%s/userinfo", opts.ProxyPrefix), | ||||||
| 
 | 
 | ||||||
| 		ProxyPrefix:         opts.ProxyPrefix, | 		ProxyPrefix:         opts.ProxyPrefix, | ||||||
| 		provider:            opts.provider, | 		provider:            opts.provider, | ||||||
|  | @ -557,6 +560,8 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | ||||||
| 		p.OAuthCallback(rw, req) | 		p.OAuthCallback(rw, req) | ||||||
| 	case path == p.AuthOnlyPath: | 	case path == p.AuthOnlyPath: | ||||||
| 		p.AuthenticateOnly(rw, req) | 		p.AuthenticateOnly(rw, req) | ||||||
|  | 	case path == p.UserInfoPath: | ||||||
|  | 		p.UserInfo(rw, req) | ||||||
| 	default: | 	default: | ||||||
| 		p.Proxy(rw, req) | 		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
 | // SignOut sends a response to clear the authentication cookie
 | ||||||
| func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { | func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { | ||||||
| 	p.ClearSessionCookie(rw, req) | 	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 { | func NewAuthOnlyEndpointTest(modifiers ...OptionsModifier) *ProcessCookieTest { | ||||||
| 	pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...) | 	pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...) | ||||||
| 	pcTest.req, _ = http.NewRequest("GET", | 	pcTest.req, _ = http.NewRequest("GET", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue