Internet Protocols · Identity Protocols

Federated Session State: Logout, Propagation, and the Limits of Single Sign-Out

Why a logout at one relying party cannot reliably end sessions at others, how back-channel and front-channel logout differ, and what session identifiers can and cannot carry.

Single sign-on has a property that its users notice only when it fails: signing in once creates sessions at several independent origins. Signing out is the inverse operation, and it is harder, because a logout at one origin cannot directly delete a cookie at another. The federation specifications define three distinct mechanisms for the inverse, and none of them is a guarantee.

Sessions live at origins, and origins do not share state

A cookie is scoped by domain. RFC 6265 defines the rules for setting and sending them, and the operative one is that a cookie for client.example is not sent to issuer.example and cannot be deleted by issuer.example. Even a cookie scoped to a parent domain is subject to the public suffix rules and to the browser’s treatment of domain matching.

This is the constraint that shapes every logout design. The relying party’s session is its own cookie. The issuer’s session is its own cookie. Neither can reach the other’s storage, and any coordination must happen through the browser making requests, or through the issuer making requests to the relying party, or through the relying party polling something.

The second constraint is that a session may be represented by more than one cookie, and by state the server holds keyed by a cookie value. Deleting the cookie without invalidating the server-side record leaves a session that can be resumed if the cookie value is captured.

The identifier the issuer gives the client

OpenID Connect Session Management defines session_state, a value returned alongside the authentication response. It is an opaque string computed by the issuer from the client’s client_id, the client’s origin, the issuer’s session state, and a salt. The client stores it and, when it wants to know whether the user is still signed in at the issuer, renders a hidden iframe pointing at the issuer’s check-session endpoint with that value, and receives a postMessage indicating changed or unchanged.

The mechanism is a workaround for the same-origin policy, not an elegant protocol. Its costs are visible: a third-party iframe on the client’s page, which some browsers restrict by default; a dependency on the issuer being reachable from the client’s page; and a value that must be recomputed and re-stored whenever the session state changes.

The value is deliberately opaque and bound to the client and origin, so it cannot be used by another relying party to probe the user’s session at the issuer. That binding is also why the client cannot derive anything from it: it is a capability to ask one question, not a data structure.

Back-channel logout: the issuer pushes

Back-channel Logout defines a different arrangement. The issuer maintains, per session, the set of clients that participated. On logout it sends a POST to each client’s registered back-channel logout URI, carrying a logout_token: a signed JWT with events containing the back-channel logout event identifier, iss, aud set to the client, iat, jti, and either sub or sid identifying the session.

The client validates the logout token as it would validate an ID token, with two differences. The token must not carry a nonce, and it must carry the event claim; the specification requires rejection in both cases, because a token that looks like an ID token but is presented at the logout endpoint could otherwise be replayed as an authentication. The sid claim is the session identifier, and it is the only value that lets a client identify a specific session rather than all sessions belonging to a subject.

The client then terminates the matching session and responds with 200. A 400 means the token was invalid and the issuer should not retry; a 5xx means the client failed transiently and the issuer may retry.

The advantage over the front-channel approach is that the issuer talks to the client directly, so the browser is not involved and no iframe is needed. The disadvantage is that the issuer must be able to reach every client, which fails for clients behind a firewall, clients that are offline, and clients that have been decommissioned but are still in the session record.

Front-channel logout: the browser carries it

Front-Channel Logout inverts the direction. On logout the issuer renders a page containing one iframe per client, each pointing at the client’s registered front-channel logout URI with iss and sid parameters. The client’s page loads inside the iframe, reads the parameters, clears its own session cookie, and the browser’s cookie rules apply because the request is a real request to the client’s origin.

The advantage is reachability: the client does not need to be reachable from the issuer, only from the user’s browser, and the user’s browser has just been at the issuer. The disadvantage is that the mechanism depends on the browser loading third-party iframes, which is exactly the behaviour that tracking-prevention features restrict. A browser that blocks third-party cookies will not send the client’s session cookie with the iframe request, so the client cannot identify which session to terminate, and the logout silently does nothing.

Neither mechanism is a superset of the other. Deployments that support both typically send back-channel first and use front-channel as a fallback, which produces the awkward property that a client may receive two logout notifications for one session and must be idempotent.

Why sid is the claim that matters

A logout token carrying only sub instructs the client to terminate every session belonging to that subject. That is broader than the event: the user may have signed in at the same client from two browsers, and the logout happened in one of them.

The sid claim identifies the issuer-side session, and the client is expected to have recorded it from the ID token at login and to terminate only the matching local session. This requires the client to store the mapping from sid to its own session, which is one more piece of state to keep consistent, and it requires the issuer to include sid in ID tokens, which Session Management defines but which is not universally present.

A client that receives a logout token with sid and terminates all sessions for the subject has over-performed; a client that ignores sid because it never stored it has no choice. The practical outcome is that logout precision depends on a claim the client may not have kept.

The timing problem

Logout propagation is asynchronous, and the window is the sum of several delays: the issuer’s dispatch, the client’s processing, and any retry backoff.

During that window the client’s session is live. A user who logs out at the issuer and then navigates directly to a relying party, without going through the issuer, presents a valid session cookie and is treated as signed in. The relying party has no way to know that the issuer’s session ended, unless it checks.

This is the residual weakness of every single sign-out design, and it is not a defect in the specifications so much as a consequence of independent origins. The mitigations are all about shortening the window rather than eliminating it: short-lived relying-party sessions that force revalidation, back-channel notification that arrives quickly, and check-session polling for clients that need current state.

Local logout is a separate operation

A client’s own logout endpoint clears its session and, in a federated deployment, may also redirect the browser to the issuer’s end-session endpoint to end the issuer session. That redirect is the only way a client can initiate a global logout, and it has a well-known open-redirect hazard: the end-session endpoint takes a post-logout redirect URI, which must be validated against the client’s registered set with the same exact-match discipline as the authorization endpoint.

The id_token_hint parameter is the recommended way to identify which session to end, because it is a signed statement from the issuer about a specific authentication event. Without it, the issuer must rely on its own session cookie to decide which session to terminate, which works when the browser still has that cookie and fails when it does not.

An issuer that terminates a session on an unauthenticated request from a third party has built a logout CSRF: an attacker can sign a user out of every application by embedding the request in a page. The end-session endpoint must therefore verify that the request is authorised, either by the presence of a valid id_token_hint for the session being ended or by an explicit user confirmation.

What a deployment can actually promise

A deployment can promise that a logout at the issuer ends the issuer’s session, and that notifications are sent to the clients that registered for them. It cannot promise that every client session ends, because a client may be unreachable, may have been decommissioned, or may be behind a browser policy that blocks the mechanism it implemented.

The honest design consequence is to bound the exposure rather than to promise immediate termination. Relying-party sessions should have their own expiry that does not depend on the issuer’s. Access tokens should be short-lived, so that a session left open by a failed logout does not also leave a working credential, which is the same bounded-staleness trade described in local token validation. And the set of clients in a session should be recorded at the issuer, because propagation requires knowing whom to notify.

The one thing that does not work is treating logout as a security boundary. Logout ends a session; it does not revoke credentials that have already left the issuer, and no amount of notification machinery changes that. Revocation is a separate mechanism, and the grant it operates on is the subject of refresh token rotation.

References

  1. OpenID Connect Session Management 1.0 — OpenID Foundation
  2. OpenID Connect Front-Channel Logout 1.0 — OpenID Foundation
  3. OpenID Connect Back-Channel Logout 1.0 — OpenID Foundation
  4. RFC 6265: HTTP State Management Mechanism — IETF