diff --git a/CHANGELOG.md b/CHANGELOG.md index 4542945f..6a284fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ## Changes since v7.14.3 +- [#3352](https://github.com/oauth2-proxy/oauth2-proxy/pull/3352) fix: backend logout URL call on sign out (#3172)(@vsejpal) + # V7.14.3 ## Release Highlights diff --git a/oauthproxy.go b/oauthproxy.go index 508084c8..d140f6a7 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -746,6 +746,10 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) return } + // Call backend logout before clearing the session so we still have the session + // (and id_token) available to invoke the provider's logout endpoint + p.backendLogout(rw, req) + err = p.ClearSessionCookie(rw, req) if err != nil { logger.Errorf("Error clearing session cookie: %v", err) @@ -753,8 +757,6 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) { return } - p.backendLogout(rw, req) - http.Redirect(rw, req, redirect, http.StatusFound) } diff --git a/oauthproxy_test.go b/oauthproxy_test.go index ccabdbbd..b33bdfe5 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -2028,6 +2028,44 @@ func Test_noCacheHeaders(t *testing.T) { }) } +func TestSignOutCallsBackendLogoutURL(t *testing.T) { + const testIDToken = "test-id-token-12345" + var receivedURL string + backendLogoutServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + receivedURL = req.URL.String() + rw.WriteHeader(http.StatusOK) + })) + defer backendLogoutServer.Close() + + opts := baseTestOptions() + opts.Providers[0].BackendLogoutURL = backendLogoutServer.URL + "/logout?id_token_hint={id_token}" + err := validation.Validate(opts) + require.NoError(t, err) + + proxy, err := NewOAuthProxy(opts, func(string) bool { return true }) + require.NoError(t, err) + + // Save a session with IDToken so backend logout can use it + rw := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/", nil) + err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{ + Email: "user@example.com", + IDToken: testIDToken, + }) + require.NoError(t, err) + cookie := rw.Header().Values("Set-Cookie")[0] + + // Hit sign_out with the session cookie; backend logout should be called before session is cleared + signOutReq := httptest.NewRequest(http.MethodGet, "/oauth2/sign_out", nil) + signOutReq.Header.Set("Cookie", cookie) + rec := httptest.NewRecorder() + proxy.ServeHTTP(rec, signOutReq) + + assert.Equal(t, http.StatusFound, rec.Code, "sign_out should redirect") + assert.Contains(t, receivedURL, "id_token_hint="+testIDToken, + "backend logout URL should have been called with id_token from session") +} + func baseTestOptions() *options.Options { opts := options.NewOptions() opts.Cookie.Secret = rawCookieSecret