Signing out in your tenant
How an application signs a person out of your tenant, and how every application a session signed in to learns that it ended.
Sessions and sid
When a person signs in on your tenant's pages, their browser gets a session with a public session ID. Every ID token issued for that sign-in carries it as sid, and the session remembers each client it signed in to, with the sub that client was given. Signing in again in the same browser as the same person keeps the session and its sid. A different person signing in there ends the first session. ID tokens from a refresh or the device grant carry no sid.
Sign-out requests from applications
An application sends the person to /oidc/logout, the end_session_endpoint in discovery, with GET or POST (OpenID Connect RP-Initiated Logout 1.0):
GET /oidc/logout?id_token_hint=<ID token>&post_logout_redirect_uri=https://app.example/signed-out&state=afterid_token_hint is an ID token your tenant issued; it may have expired. client_id names the application when there is no hint. post_logout_redirect_uri must be one of the client's registered Return addresses after sign-out, exactly, and the person returns there with state. Anything else stops on a page that names the problem, such as invalid_post_logout_redirect_uri or invalid_id_token_hint, and nobody is signed out.
By default the person is asked to confirm, unless the hint names the session in this browser. Flow Policy can make the question appear every time. A confirmation only counts when posted from your tenant's own page, so another site cannot sign people out without them. A sign-out started here does not yet reach the person's SAML applications; signing out on your tenant's own pages does, as SAML single logout describes.
Front-channel logout
A client with a Front-channel logout URI is loaded in a hidden frame on the signed-out page, with iss and sid added (Front-Channel Logout 1.0). The address must have the scheme, host and port of one of the client's redirect URIs. The page then moves on to the return address after two seconds, or offers a link. Front-channel logout runs only when the person signs out through /oidc/logout, because it needs their browser. Browsers that block third-party cookies may stop a framed application from finding its own session.
Back-channel logout
A client with a Back-channel logout URI receives a POST with logout_token=<JWT> whenever a session it signed in to ends, for any reason: a sign-out request, the person's Sign out, the idle timeout, an administrator ending sessions, a password change, a lock, or another person signing in in the same browser. A session that simply reaches the end of its lifetime is not reported.
The logout token is signed with the key of the client's ID tokens and has typ logout+jwt, iss, aud (the client ID), iat, exp two minutes later, a new jti, sub, sid and events with the back-channel logout member, and never a nonce (Back-Channel Logout 1.0). Validate it with btl-lab verify --type logout.
Your tenant sends it from its own store, not from the person's browser. A 2xx answer is delivered. A 4xx answer other than 408 and 429 means the client refused it, so it is not sent again. Anything else, or no answer within five seconds, is tried again after 30 seconds, 2 minutes, 10 minutes and an hour, each time with a new token, then abandoned. The address must be https on a public host name outside Beyond the Login, or the lab receiver. Back-channel logout does not revoke tokens: the application decides what ending its own session means.
The session check frame
With the session check frame on in Flow Policy, discovery lists check_session_iframe and authorization responses carry session_state (Session Management 1.0). An application frames /oidc/check_session and posts client_id session_state to it; the frame answers unchanged, changed or error. It works from a cookie your tenant's pages set beside the session, which browsers that block third-party cookies hide from the frame, so the logout channels above are more reliable. It is off by default.
When the frame reports changed, check again with an authorization request with prompt=none as a top-level redirect, not from a hidden frame on the application's own site. Your tenant's sign-in cookie is SameSite=Lax, so a browser never sends it to a frame from another site, and a prompt=none request from such a frame always answers login_required, even for a person who is signed in.
The lab receiver
Learners' tenants have a lab receiver on their own host, listed on the Sample API page. Register its back-channel address, /lab-receiver/backchannel, or its front-channel address, /lab-receiver/frontchannel, on a client. Your tenant delivers to it without a network request. It checks each logout token as a relying party should (signature, type, issuer, audience, times, event, subject, no nonce, an unused jti), keeps what it found for a day without the token, and answers 200 for a good token and 400 for a bad one. You can make it answer 503 or 400 to the next delivery, to watch a retry or a refusal. Anyone can post a logout token to its back-channel address, as to a real application's. The BTL organization has no lab receiver.
Settings, Audit and Logs
Flow Policy turns sign-out requests, front-channel and back-channel logout and the session check frame on or off, and chooses when to ask before signing out. The client settings are under Signing out in each client. Changing either needs the usual Flow Policy or client permission and is recorded in OAuth management Audit by setting name. Each sign-out request is recorded in protocol Audit as oidc.logout, each delivery attempt as oidc.backchannel_logout with its client, user, attempt number and answer, and each message the lab receiver checked as lab.receiver, never with the token. The BTL organization has the same settings for its own clients.