Sequence
- Confirm and quiesce. The user confirms logout. The shell first instructs every embedded child application to log itself out and waits until all of them report completion. Nothing auth-related happens until this phase finishes.
- Read the ID token from the SDK's token store. If there isn't one, skip the IdP round trip: clear local state and go straight to the shared logout landing page.
-
Navigate the top window to the IdP's end-session endpoint, with two parameters:
-
id_token_hint— the current ID token, so the IdP knows which session to end -
post_logout_redirect_uri— the app's own logout-callback route
-
Local tokens are deliberately not cleared at this point.
- The IdP destroys its session cookie and redirects the browser back to the registered callback route.
- The callback route performs the cleanup: it reads the ID token out of storage one last time, clears local and session storage (preserving an explicitly allow-listed key prefix), then redirects to the shared logout landing page, forwarding the ID token as a hint so that page can finish any downstream sign-out.
Why the order is what it is
- Child apps go first. Step 3 unloads the document; anything not already finished would be lost.
-
Tokens are cleared last, not first. The ID token is needed twice after the decision to log out: once as
id_token_hintfor the IdP, and again as a hint for the downstream landing page. Clearing before leaving would break both. - Only a top-level navigation really ends the IdP session. A background fetch or hidden iframe can't reliably clear a session cookie under third-party cookie restrictions; a full-page redirect can.
Preconditions at the IdP
| Requirement | Why |
|---|---|
| The logout-callback URI is registered as a post-logout redirect URI on the client | an unregistered value is rejected and the user is stranded at the IdP |
The end-session endpoint accepts id_token_hint
|
without it the IdP may show an interactive "which session?" prompt instead of redirecting |
| The ID token is still valid and unexpired | an expired hint can be refused, breaking the redirect back |
Failure modes
| What happens | Result | Mitigation |
|---|---|---|
| The IdP never redirects back (unregistered URI, rejected hint, user closes the tab) | local tokens are left behind — the IdP session is gone but the app still looks signed in until the tokens expire | treat the callback as best-effort: also clear tokens on next app start if an "intent to log out" marker is present |
| No ID token available at step 2 | the IdP session is not ended, only local state is cleared | acceptable fallback, but log it — the user remains signed in at the IdP |
| The callback route fails to render (routing or gating issues) | cleanup never runs | the callback must be a dependency-free route: no feature flags, no app data, no auth state |
| Clearing whole-origin storage | every other tab of the app is logged out too | intended for logout, but scope the clear to your own key prefixes so unrelated state and in-flight flows elsewhere aren't destroyed |
id_token_hint passed in a query string |
the token appears in access logs, referrer headers and browser history | prefer a POST form submission to the end-session endpoint where supported, or accept and document the exposure |
Checklist
- [ ] Post-logout redirect URI registered at the IdP for every environment
- [ ] Child/embedded applications logged out before the IdP navigation
- [ ] ID token read before any storage is cleared
- [ ] Callback route has zero dependencies and always clears, even on error
- [ ] Storage clear is scoped to known key prefixes, not the whole origin
- [ ] A fallback path exists for "no ID token" and for "never came back"
- [ ] Logout tested in a second tab, with third-party cookies blocked, and with an already-expired session
Want this saved as a file or published as a shareable page?
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.