Session aware logout, backend logout url approach (#1876)
* Session aware logout, backend logout url approach * Add CHANGELOG.md and documentation for #1876 * Proper http handling and case change for golint compliance * Update alpha_config.md * Fix case conformity * Change placeholder from ${id_token} to {id_token} As this should be specified in a URL and curly braces should be escaped as %7b and %7d, therefore using {} shouldn't be an issue * Apply suggestions from code review Co-authored-by: Jan Larwig <jan@larwig.com> * Add other suggestions * Add suggestions and move background logout to generic provider * Changelog updated * Update oauthproxy.go Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Add comment for gosec, remove sensitive data from log --------- Co-authored-by: Jan Larwig <jan@larwig.com> Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
This commit is contained in:
		
							parent
							
								
									6c2c115d30
								
							
						
					
					
						commit
						e7d20519df
					
				|  | @ -20,6 +20,7 @@ | |||
| - [#2282](https://github.com/oauth2-proxy/oauth2-proxy/pull/2282) Fixed checking Google Groups membership using Google Application Credentials (@kvanzuijlen) | ||||
| - [#2183](https://github.com/oauth2-proxy/oauth2-proxy/pull/2183) Allowing relative redirect url though an option (@axel7083) | ||||
| - [#1866](https://github.com/oauth2-proxy/oauth2-proxy/pull/1866) Add support for unix socker as upstream (@babs) | ||||
| - [#1876](https://github.com/oauth2-proxy/oauth2-proxy/pull/1876) Add `--backend-logout-url` with `{id_token}` placeholder (@babs) | ||||
| - [#1949](https://github.com/oauth2-proxy/oauth2-proxy/pull/1949) Allow cookie names with dots in redis sessions (@miguelborges99) | ||||
| - [#2297](https://github.com/oauth2-proxy/oauth2-proxy/pull/2297) Add nightly build and push (@tuunit) | ||||
| - [#2329](https://github.com/oauth2-proxy/oauth2-proxy/pull/2329) Add an option to skip request to profile URL for resolving missing claims in id_token (@nilsgstrabo) | ||||
|  |  | |||
|  | @ -440,6 +440,7 @@ Provider holds all configuration for a single provider | |||
| | `scope` | _string_ | Scope is the OAuth scope specification | | ||||
| | `allowedGroups` | _[]string_ | AllowedGroups is a list of restrict logins to members of this group | | ||||
| | `code_challenge_method` | _string_ | The code challenge method | | ||||
| | `backendLogoutURL` | _string_ | URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session | | ||||
| 
 | ||||
| ### ProviderType | ||||
| #### (`string` alias) | ||||
|  |  | |||
|  | @ -74,6 +74,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/ | |||
| | `--auth-logging-format` | string | Template for authentication log lines | see [Logging Configuration](#logging-configuration) | | ||||
| | `--authenticated-emails-file` | string | authenticate against emails via file (one per line) | | | ||||
| | `--azure-tenant` | string | go to a tenant-specific or common (tenant-independent) endpoint. | `"common"` | | ||||
| | `--backend-logout-url` | string | URL to perform backend logout, if you use `{id_token}` in the url it will be replaced by the actual `id_token` of the user session | | | ||||
| | `--basic-auth-password` | string | the password to set when passing the HTTP Basic Auth header | | | ||||
| | `--client-id` | string | the OAuth Client ID, e.g. `"123456.apps.googleusercontent.com"` | | | ||||
| | `--client-secret` | string | the OAuth Client Secret | | | ||||
|  |  | |||
|  | @ -334,15 +334,15 @@ func (p *OAuthProxy) buildProxySubrouter(s *mux.Router) { | |||
| 	s.Use(prepareNoCacheMiddleware) | ||||
| 
 | ||||
| 	s.Path(signInPath).HandlerFunc(p.SignIn) | ||||
| 	s.Path(signOutPath).HandlerFunc(p.SignOut) | ||||
| 	s.Path(oauthStartPath).HandlerFunc(p.OAuthStart) | ||||
| 	s.Path(oauthCallbackPath).HandlerFunc(p.OAuthCallback) | ||||
| 
 | ||||
| 	// Static file paths
 | ||||
| 	s.PathPrefix(staticPathPrefix).Handler(http.StripPrefix(p.ProxyPrefix, http.FileServer(http.FS(staticFiles)))) | ||||
| 
 | ||||
| 	// The userinfo endpoint needs to load sessions before handling the request
 | ||||
| 	// The userinfo and logout endpoints needs to load sessions before handling the request
 | ||||
| 	s.Path(userInfoPath).Handler(p.sessionChain.ThenFunc(p.UserInfo)) | ||||
| 	s.Path(signOutPath).Handler(p.sessionChain.ThenFunc(p.SignOut)) | ||||
| } | ||||
| 
 | ||||
| // buildPreAuthChain constructs a chain that should process every request before
 | ||||
|  | @ -746,9 +746,43 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { | |||
| 		p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	p.backendLogout(rw, req) | ||||
| 
 | ||||
| 	http.Redirect(rw, req, redirect, http.StatusFound) | ||||
| } | ||||
| 
 | ||||
| func (p *OAuthProxy) backendLogout(rw http.ResponseWriter, req *http.Request) { | ||||
| 	session, err := p.getAuthenticatedSession(rw, req) | ||||
| 	if err != nil { | ||||
| 		logger.Errorf("error getting authenticated session during backend logout: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if session == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	providerData := p.provider.Data() | ||||
| 	if providerData.BackendLogoutURL == "" { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	backendLogoutURL := strings.ReplaceAll(providerData.BackendLogoutURL, "{id_token}", session.IDToken) | ||||
| 	// security exception because URL is dynamic ({id_token} replacement) but
 | ||||
| 	// base is not end-user provided but comes from configuration somewhat secure
 | ||||
| 	resp, err := http.Get(backendLogoutURL) // #nosec G107
 | ||||
| 	if err != nil { | ||||
| 		logger.Errorf("error while calling backend logout: %v", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	defer resp.Body.Close() | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		logger.Errorf("error while calling backend logout url, returned error code %v", resp.StatusCode) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // OAuthStart starts the OAuth2 authentication flow
 | ||||
| func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) { | ||||
| 	// start the flow permitting login URL query parameters to be overridden from the request URL
 | ||||
|  |  | |||
|  | @ -532,6 +532,7 @@ type LegacyProvider struct { | |||
| 	UserIDClaim                        string   `flag:"user-id-claim" cfg:"user_id_claim"` | ||||
| 	AllowedGroups                      []string `flag:"allowed-group" cfg:"allowed_groups"` | ||||
| 	AllowedRoles                       []string `flag:"allowed-role" cfg:"allowed_roles"` | ||||
| 	BackendLogoutURL                   string   `flag:"backend-logout-url" cfg:"backend_logout_url"` | ||||
| 
 | ||||
| 	AcrValues  string `flag:"acr-values" cfg:"acr_values"` | ||||
| 	JWTKey     string `flag:"jwt-key" cfg:"jwt_key"` | ||||
|  | @ -596,6 +597,7 @@ func legacyProviderFlagSet() *pflag.FlagSet { | |||
| 	flagSet.String("user-id-claim", OIDCEmailClaim, "(DEPRECATED for `oidc-email-claim`) which claim contains the user ID") | ||||
| 	flagSet.StringSlice("allowed-group", []string{}, "restrict logins to members of this group (may be given multiple times)") | ||||
| 	flagSet.StringSlice("allowed-role", []string{}, "(keycloak-oidc) restrict logins to members of these roles (may be given multiple times)") | ||||
| 	flagSet.String("backend-logout-url", "", "url to perform a backend logout, {id_token} can be used as placeholder for the id_token") | ||||
| 
 | ||||
| 	return flagSet | ||||
| } | ||||
|  | @ -675,6 +677,7 @@ func (l *LegacyProvider) convert() (Providers, error) { | |||
| 		Scope:                    l.Scope, | ||||
| 		AllowedGroups:            l.AllowedGroups, | ||||
| 		CodeChallengeMethod:      l.CodeChallengeMethod, | ||||
| 		BackendLogoutURL:         l.BackendLogoutURL, | ||||
| 	} | ||||
| 
 | ||||
| 	// This part is out of the switch section for all providers that support OIDC
 | ||||
|  |  | |||
|  | @ -83,6 +83,9 @@ type Provider struct { | |||
| 	AllowedGroups []string `json:"allowedGroups,omitempty"` | ||||
| 	// The code challenge method
 | ||||
| 	CodeChallengeMethod string `json:"code_challenge_method,omitempty"` | ||||
| 
 | ||||
| 	// URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session
 | ||||
| 	BackendLogoutURL string `json:"backendLogoutURL"` | ||||
| } | ||||
| 
 | ||||
| // ProviderType is used to enumerate the different provider type options
 | ||||
|  |  | |||
|  | @ -57,6 +57,8 @@ type ProviderData struct { | |||
| 	getAuthorizationHeaderFunc func(string) http.Header | ||||
| 	loginURLParameterDefaults  url.Values | ||||
| 	loginURLParameterOverrides map[string]*regexp.Regexp | ||||
| 
 | ||||
| 	BackendLogoutURL string | ||||
| } | ||||
| 
 | ||||
| // Data returns the ProviderData
 | ||||
|  |  | |||
|  | @ -159,6 +159,8 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData, | |||
| 
 | ||||
| 	p.setAllowedGroups(providerConfig.AllowedGroups) | ||||
| 
 | ||||
| 	p.BackendLogoutURL = providerConfig.BackendLogoutURL | ||||
| 
 | ||||
| 	return p, nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue