git.iko.soy/typeable/oauth2-proxy
on master help sign in
feat: possibility to inject id_token in redirect url during sign out (#3278)
3085309Alban Fonrouge
CHANGELOG.md+1
@@ -11,6 +11,7 @@
11- [#3352](https://github.com/oauth2-proxy/oauth2-proxy/pull/3352) fix: backend logout URL call on sign out (#3172)(@vsejpal)
11- [#3352](https://github.com/oauth2-proxy/oauth2-proxy/pull/3352) fix: backend logout URL call on sign out (#3172)(@vsejpal)
12- [#3332](https://github.com/oauth2-proxy/oauth2-proxy/pull/3332) ci: distribute windows binary with .exe extension (@igitur)
12- [#3332](https://github.com/oauth2-proxy/oauth2-proxy/pull/3332) ci: distribute windows binary with .exe extension (@igitur)
13- [#2685](https://github.com/oauth2-proxy/oauth2-proxy/pull/2685) feat: allow arbitrary claims from the IDToken and IdentityProvider UserInfo endpoint to be added to the session state (@vegetablest)
13- [#2685](https://github.com/oauth2-proxy/oauth2-proxy/pull/2685) feat: allow arbitrary claims from the IDToken and IdentityProvider UserInfo endpoint to be added to the session state (@vegetablest)
14+- [#3278](https://github.com/oauth2-proxy/oauth2-proxy/pull/3278) feat: possibility to inject id_token in redirect url during sign out (@albanf)
14
15
15# V7.14.3
16# V7.14.3
16
17
docs/docs/features/endpoints.md+14
@@ -38,6 +38,20 @@ X-Auth-Request-Redirect: https://my-oidc-provider/sign_out_page
38
38
39BEWARE that the domain you want to redirect to (`my-oidc-provider.example.com` in the example) must be added to the [`--whitelist-domain`](../configuration/overview) configuration option otherwise the redirect will be ignored. Make sure to include the actual domain and port (if needed) and not the URL (e.g "localhost:8081" instead of "http://localhost:8081").
39BEWARE that the domain you want to redirect to (`my-oidc-provider.example.com` in the example) must be added to the [`--whitelist-domain`](../configuration/overview) configuration option otherwise the redirect will be ignored. Make sure to include the actual domain and port (if needed) and not the URL (e.g "localhost:8081" instead of "http://localhost:8081").
40
40
41+ID Token can be injected in the redirect url by using `{id_token}` placeholder. For example to redirect to `https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com`;
42+
43+```
44+/oauth2/sign_out?rd=https%3A%2F%2Fmy-oidc-provider.example.com%2Fsign_out_page%3Fid_token_hint%3D%7Bid_token%7D%26post_logout_redirect_uri%3Dhttps%3A%2F%2Fmy-app.example.com
45+```
46+
47+or alternatively in the header:
48+
49+```
50+GET /oauth2/sign_out HTTP/1.1
51+X-Auth-Request-Redirect: https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com
52+...
53+```
54+
41### Auth
55### Auth
42
56
43This endpoint returns 202 Accepted response or a 401 Unauthorized response.
57This endpoint returns 202 Accepted response or a 401 Unauthorized response.
oauthproxy.go+13−1
@@ -54,6 +54,8 @@ const (
54 authOnlyPath = "/auth"
54 authOnlyPath = "/auth"
55 userInfoPath = "/userinfo"
55 userInfoPath = "/userinfo"
56 staticPathPrefix = "/static/"
56 staticPathPrefix = "/static/"
57+
58+ idTokenPlaceholder = "{id_token}"
57)
59)
58
60
59var (
61var (
@@ -748,6 +750,16 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
748 p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
750 p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
749 return
751 return
750 }
752 }
753+
754+ if strings.Contains(redirect, idTokenPlaceholder) {
755+ session, err := p.getAuthenticatedSession(rw, req)
756+ if err != nil {
757+ logger.Errorf("error getting authenticated session during SignOut, won't replace id_token placeholder in redirect URL: %v", err)
758+ } else {
759+ redirect = strings.ReplaceAll(redirect, idTokenPlaceholder, session.IDToken)
760+ }
761+ }
762+
751 // Call backend logout before clearing the session so we still have the session
763 // Call backend logout before clearing the session so we still have the session
752 // (and id_token) available to invoke the provider's logout endpoint
764 // (and id_token) available to invoke the provider's logout endpoint
753 p.backendLogout(rw, req)
765 p.backendLogout(rw, req)
@@ -778,7 +790,7 @@ func (p *OAuthProxy) backendLogout(rw http.ResponseWriter, req *http.Request) {
778 return
790 return
779 }
791 }
780
792
781− backendLogoutURL := strings.ReplaceAll(providerData.BackendLogoutURL, "{id_token}", session.IDToken)
793+ backendLogoutURL := strings.ReplaceAll(providerData.BackendLogoutURL, idTokenPlaceholder, session.IDToken)
782 // security exception because URL is dynamic ({id_token} replacement) but
794 // security exception because URL is dynamic ({id_token} replacement) but
783 // base is not end-user provided but comes from configuration somewhat secure
795 // base is not end-user provided but comes from configuration somewhat secure
784 resp, err := http.Get(backendLogoutURL) // #nosec G107
796 resp, err := http.Get(backendLogoutURL) // #nosec G107
oauthproxy_test.go+47
@@ -15,6 +15,7 @@ import (
15 "time"
15 "time"
16
16
17 "github.com/coreos/go-oidc/v3/oidc"
17 "github.com/coreos/go-oidc/v3/oidc"
18+ middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
18 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
19 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
19 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
20 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
20 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/hmacauth"
21 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/hmacauth"
@@ -3583,3 +3584,49 @@ func TestGetOAuthRedirectURI(t *testing.T) {
3583 })
3584 })
3584 }
3585 }
3585}
3586}
3587+
3588+func TestIdTokenPlaceholderInSignOut(t *testing.T) {
3589+ opts := baseTestOptions()
3590+ opts.WhitelistDomains = []string{"my-oidc-provider.example.com"}
3591+
3592+ err := validation.Validate(opts)
3593+ assert.NoError(t, err)
3594+
3595+ const emailAddress = "john.doe@example.com"
3596+ const userName = "9fcab5c9b889a557"
3597+ created := time.Now()
3598+
3599+ session := &sessions.SessionState{
3600+ User: userName,
3601+ Groups: []string{"a", "b"},
3602+ Email: emailAddress,
3603+ IDToken: "eYjjjjjj.vvvv.ddd",
3604+ AccessToken: "oauth_token",
3605+ CreatedAt: &created,
3606+ }
3607+
3608+ proxy, err := NewOAuthProxy(opts, func(email string) bool {
3609+ return true
3610+ })
3611+ assert.NoError(t, err)
3612+
3613+ // Save the required session
3614+ rw := httptest.NewRecorder()
3615+ req, _ := http.NewRequest("GET", "/", nil)
3616+ err = proxy.sessionStore.Save(rw, req, session)
3617+ assert.NoError(t, err)
3618+
3619+ rw = httptest.NewRecorder()
3620+
3621+ rdUrl := url.QueryEscape("https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com/")
3622+ req, _ = http.NewRequest("GET", "/oauth2/sign_out?rd="+rdUrl, nil)
3623+ req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
3624+ RequestID: "11111111-2222-4333-8444-555555555555",
3625+ Session: session,
3626+ })
3627+
3628+ proxy.SignOut(rw, req)
3629+ newLocation := rw.Header().Values("Location")[0]
3630+
3631+ assert.Equal(t, "https://my-oidc-provider.example.com/sign_out_page?id_token_hint=eYjjjjjj.vvvv.ddd&post_logout_redirect_uri=https://my-app.example.com/", newLocation)
3632+}