Add id_token_hint support#5814
Open
tung2744 wants to merge 17 commits into
Open
Conversation
The end_session_endpoint silently falls back to the Settings page on an invalid post_logout_redirect_uri instead of returning an error. Document this as intentional API debt kept to avoid a breaking change, not the ideal behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Plans the missing id_token_hint handling for the RP-initiated logout endpoint: a stateless POST stash to keep the hint out of the URL, and resolving the hint's session by SSO group membership rather than sid equality.
A POST to end_session can't reliably see the Lax session cookie, so the request needs to be stashed and replayed as a same-origin GET. Sealing it with a random per-request AES-256-GCM key carried in a short-lived cookie avoids exposing id_token_hint in the redirect URL without needing a server-side store.
The confirmation page redirect needs to forward the rest of the request without re-exposing id_token_hint in the new URL.
Adds the spec's id_token_hint fast path: if the hint's sid resolves to a session or offline grant in the same SSO group as the current IdP session, and the client is first-party, log out directly instead of showing the confirmation page. SSO group membership is used rather than sid equality, since a first-party client's normal offline_access grant is bound to its own refresh-token session, not the IDP session cookie directly. A POST is stashed and redirected to itself as a GET first, so the Lax session cookie is visible before the decision is made. An invalid or expired stash (e.g. a stale link from browser history) is logged and treated as a parameterless request rather than surfacing as an error. Once id_token_hint is given at all, its resolution is the sole authority on the direct-logout decision; the pre-existing SameSiteStrict fast path is only consulted when no hint is present, so it can no longer silently override a hint that failed to match.
Adds what's needed to exercise id_token_hint end-to-end: exposing the raw ID token from an exchange_code result, options to drive a second OAuth client and enable SSO session cookies during setup, an action to approve the OAuth consent screen, and an action to clear specific cookies (the test client's jar doesn't model SameSite, so a same-site-only cookie set during login otherwise leaks into later requests). Also adds a shared third-party client fixture. All additions are backward compatible: existing callers keep their current defaults.
Covers: first-party client with a matching hint (GET and POST/stash round trip), third-party client (confirmation page despite a matching hint), SSO-group mismatch, a malformed hint, no session at all, and an invalid/expired POST stash. All cases use the normal offline_access + exchange_code flow to obtain id_token_hint, matching how a real client would.
…2e tests Every case only asserted the response's own status/redirect target, never that a session or offline grant was actually deleted from (or preserved in) storage. Add a refresh_token field to the exchange_code result and, after each logout or no-op assertion, present it to an independent code path (the refresh_token grant, which looks the grant up from storage) expecting invalid_grant after a real logout or 200 after a confirmed no-op. This also surfaced that logging in as a second user while the client's jar still carried the first user's session cookie causes the server to delete that session and its offline grant outright, unrelated to end_session, which had been letting the sid-mismatch case pass for the wrong reason (the hint failing to resolve at all, not resolving to a different SSO group). Fixed by resetting the cookie jar between logins.
Documents the refresh_token-based revocation checks and the "one login per browser" behavior discovered while adding them, per §13.1 points 9-10.
…mation Case titles and comments still described the pre-redesign sid-equality check even though the production comparison moved to SSO-group membership; renamed to match. Also add a case simulating a human actually confirming logout on the /logout page: no case exercised that handler before, only that the page was reached.
…onsumed Both properties already existed (a 5-minute MaxAge, and an unconditional clear on every resumed request via resumeFromStash's defer) but were only implicitly covered by response-code assertions. Make them explicit so a regression here would fail loudly.
The cookie's actual on-wire name didn't read as related to x_end_session_ref, the query param it pairs with. Renamed so the two are recognizable together, and to better reflect that the cookie holds the key decrypting the query's sealed value, not "the stash" itself.
…Enabled An ordinary OIDC client that never sends x_sso_enabled (the common case, since that's an Authgear-specific extension) still authenticates through a real IDP session, but its offline grant's SSOEnabled defaults to false, so IsSameSSOGroup always denied it the silent-logout fast path even though IDPSessionID demonstrably named that exact session. OfflineGrant.IsSameSSOGroup no longer gates its own (the grant's) side of the comparison on SSOEnabled; only raw IDPSessionID equality is checked. The argument's side is untouched, so grant-to-grant comparisons still correctly require both sides to have opted in. session.Manager.invalidate reuses this same method, so revoking the IDP session now also revokes this grant automatically, with no special casing needed in end_session's own handler. This also affects sessionlisting.go's "current device" flag for the same grant, which is the same fix applied consistently, not a regression: updated its test to match. Adds SetupOAuthOptions.SSOEnabledOmitted to the e2e harness (the existing option always sent x_sso_enabled explicitly, true or false, so there was no way to construct a request that omits it entirely) and a test reproducing the exact scenario end-to-end, verified against a live server.
…up fix RP-Initiated Logout's id_token_hint section previously said "its sid matches the current logged in IdP session" without defining what "matches" means; spell out the two cases (the session itself, or a refresh token grant whose idp_session_id names it, regardless of x_sso_enabled). Updates oidc-sso-browser.md's Logout and Session listing sections to reflect that revoking an IdP session now sweeps every refresh token grant created from it regardless of that grant's own sso_enabled.
Documents the live HAR-trace debugging session, the two alternative designs considered and rejected (a separate IsCreatedFromSession check with a Handle-level double-Logout call; deriving SSOEnabled from IDPSessionID at token-issuance time), and the final IsSameSSOGroup fix, including its sessionlisting.go consequence.
tung2744
force-pushed
the
dev-3722-rp-init-logout
branch
from
July 24, 2026 08:39
ffc3c56 to
1ebc85c
Compare
Contributor
Author
|
Rebased, thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
requires
ref DEV-3722
How to test:
http://localhost:15001/callbackas redirect uri,http://localhost:15001/as post_logout_redirect_uri.OIDC_END_SESSION_METHODtoPOSTto test for POST methodPoints to note:
idp_session_idset is by the continue screen, and user only see continue screen if sso is enabled. But we can discuss on this if you see any concern.