git.iko.soy/typeable/oauth2-proxy
on master help sign in
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>

commite7d20519df30ad1cb1b8e34f94ffd6600de4a238 authorDamien Degois <damien@degois.info> dateFri 26 Jan 2024, 13:48 (3 years ago) parent6c2c115
8 files changed+49−2
CHANGELOG.md+1 docs/docs/configuration/alpha_config.md+1 docs/docs/configuration/overview.md+1 oauthproxy.go+36−2 pkg/apis/options/legacy_options.go+3 pkg/apis/options/providers.go+3 providers/provider_data.go+2 providers/providers.go+2
CHANGELOG.md+1
@@ -20,6 +20,7 @@
2020- [#2282](https://github.com/oauth2-proxy/oauth2-proxy/pull/2282) Fixed checking Google Groups membership using Google Application Credentials (@kvanzuijlen)
2121- [#2183](https://github.com/oauth2-proxy/oauth2-proxy/pull/2183) Allowing relative redirect url though an option (@axel7083)
2222- [#1866](https://github.com/oauth2-proxy/oauth2-proxy/pull/1866) Add support for unix socker as upstream (@babs)
23+- [#1876](https://github.com/oauth2-proxy/oauth2-proxy/pull/1876) Add `--backend-logout-url` with `{id_token}` placeholder (@babs)
2324- [#1949](https://github.com/oauth2-proxy/oauth2-proxy/pull/1949) Allow cookie names with dots in redis sessions (@miguelborges99)
2425- [#2297](https://github.com/oauth2-proxy/oauth2-proxy/pull/2297) Add nightly build and push (@tuunit)
2526- [#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)
docs/docs/configuration/alpha_config.md+1
@@ -440,6 +440,7 @@ Provider holds all configuration for a single provider
440440| `scope` | _string_ | Scope is the OAuth scope specification |
441441| `allowedGroups` | _[]string_ | AllowedGroups is a list of restrict logins to members of this group |
442442| `code_challenge_method` | _string_ | The code challenge method |
443+| `backendLogoutURL` | _string_ | URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session |
443444
444445### ProviderType
445446#### (`string` alias)
docs/docs/configuration/overview.md+1
@@ -74,6 +74,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
7474| `--auth-logging-format` | string | Template for authentication log lines | see [Logging Configuration](#logging-configuration) |
7575| `--authenticated-emails-file` | string | authenticate against emails via file (one per line) | |
7676| `--azure-tenant` | string | go to a tenant-specific or common (tenant-independent) endpoint. | `"common"` |
77+| `--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 | |
7778| `--basic-auth-password` | string | the password to set when passing the HTTP Basic Auth header | |
7879| `--client-id` | string | the OAuth Client ID, e.g. `"123456.apps.googleusercontent.com"` | |
7980| `--client-secret` | string | the OAuth Client Secret | |
oauthproxy.go+36−2
@@ -334,15 +334,15 @@ func (p *OAuthProxy) buildProxySubrouter(s *mux.Router) {
334334 s.Use(prepareNoCacheMiddleware)
335335
336336 s.Path(signInPath).HandlerFunc(p.SignIn)
337− s.Path(signOutPath).HandlerFunc(p.SignOut)
338337 s.Path(oauthStartPath).HandlerFunc(p.OAuthStart)
339338 s.Path(oauthCallbackPath).HandlerFunc(p.OAuthCallback)
340339
341340 // Static file paths
342341 s.PathPrefix(staticPathPrefix).Handler(http.StripPrefix(p.ProxyPrefix, http.FileServer(http.FS(staticFiles))))
343342
344− // The userinfo endpoint needs to load sessions before handling the request
343+ // The userinfo and logout endpoints needs to load sessions before handling the request
345344 s.Path(userInfoPath).Handler(p.sessionChain.ThenFunc(p.UserInfo))
345+ s.Path(signOutPath).Handler(p.sessionChain.ThenFunc(p.SignOut))
346346}
347347
348348// buildPreAuthChain constructs a chain that should process every request before
@@ -746,9 +746,43 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
746746 p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
747747 return
748748 }
749+
750+ p.backendLogout(rw, req)
751+
749752 http.Redirect(rw, req, redirect, http.StatusFound)
750753}
751754
755+func (p *OAuthProxy) backendLogout(rw http.ResponseWriter, req *http.Request) {
756+ session, err := p.getAuthenticatedSession(rw, req)
757+ if err != nil {
758+ logger.Errorf("error getting authenticated session during backend logout: %v", err)
759+ return
760+ }
761+
762+ if session == nil {
763+ return
764+ }
765+
766+ providerData := p.provider.Data()
767+ if providerData.BackendLogoutURL == "" {
768+ return
769+ }
770+
771+ backendLogoutURL := strings.ReplaceAll(providerData.BackendLogoutURL, "{id_token}", session.IDToken)
772+ // security exception because URL is dynamic ({id_token} replacement) but
773+ // base is not end-user provided but comes from configuration somewhat secure
774+ resp, err := http.Get(backendLogoutURL) // #nosec G107
775+ if err != nil {
776+ logger.Errorf("error while calling backend logout: %v", err)
777+ return
778+ }
779+
780+ defer resp.Body.Close()
781+ if resp.StatusCode != 200 {
782+ logger.Errorf("error while calling backend logout url, returned error code %v", resp.StatusCode)
783+ }
784+}
785+
752786// OAuthStart starts the OAuth2 authentication flow
753787func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
754788 // start the flow permitting login URL query parameters to be overridden from the request URL
pkg/apis/options/legacy_options.go+3
@@ -532,6 +532,7 @@ type LegacyProvider struct {
532532 UserIDClaim string `flag:"user-id-claim" cfg:"user_id_claim"`
533533 AllowedGroups []string `flag:"allowed-group" cfg:"allowed_groups"`
534534 AllowedRoles []string `flag:"allowed-role" cfg:"allowed_roles"`
535+ BackendLogoutURL string `flag:"backend-logout-url" cfg:"backend_logout_url"`
535536
536537 AcrValues string `flag:"acr-values" cfg:"acr_values"`
537538 JWTKey string `flag:"jwt-key" cfg:"jwt_key"`
@@ -596,6 +597,7 @@ func legacyProviderFlagSet() *pflag.FlagSet {
596597 flagSet.String("user-id-claim", OIDCEmailClaim, "(DEPRECATED for `oidc-email-claim`) which claim contains the user ID")
597598 flagSet.StringSlice("allowed-group", []string{}, "restrict logins to members of this group (may be given multiple times)")
598599 flagSet.StringSlice("allowed-role", []string{}, "(keycloak-oidc) restrict logins to members of these roles (may be given multiple times)")
600+ flagSet.String("backend-logout-url", "", "url to perform a backend logout, {id_token} can be used as placeholder for the id_token")
599601
600602 return flagSet
601603}
@@ -675,6 +677,7 @@ func (l *LegacyProvider) convert() (Providers, error) {
675677 Scope: l.Scope,
676678 AllowedGroups: l.AllowedGroups,
677679 CodeChallengeMethod: l.CodeChallengeMethod,
680+ BackendLogoutURL: l.BackendLogoutURL,
678681 }
679682
680683 // This part is out of the switch section for all providers that support OIDC
pkg/apis/options/providers.go+3
@@ -83,6 +83,9 @@ type Provider struct {
8383 AllowedGroups []string `json:"allowedGroups,omitempty"`
8484 // The code challenge method
8585 CodeChallengeMethod string `json:"code_challenge_method,omitempty"`
86+
87+ // URL to call to perform backend logout, `{id_token}` would be replaced by the actual `id_token` if available in the session
88+ BackendLogoutURL string `json:"backendLogoutURL"`
8689}
8790
8891// ProviderType is used to enumerate the different provider type options
providers/provider_data.go+2
@@ -57,6 +57,8 @@ type ProviderData struct {
5757 getAuthorizationHeaderFunc func(string) http.Header
5858 loginURLParameterDefaults url.Values
5959 loginURLParameterOverrides map[string]*regexp.Regexp
60+
61+ BackendLogoutURL string
6062}
6163
6264// Data returns the ProviderData
providers/providers.go+2
@@ -159,6 +159,8 @@ func newProviderDataFromConfig(providerConfig options.Provider) (*ProviderData,
159159
160160 p.setAllowedGroups(providerConfig.AllowedGroups)
161161
162+ p.BackendLogoutURL = providerConfig.BackendLogoutURL
163+
162164 return p, nil
163165}
164166