Back-channel logout
Receive OIDC Back-Channel Logout tokens — registering the URL, choosing which session endings you are told about, the logout token's claims, delivery and retries, and the checks your endpoint must make.
Clearing your own session cookie when the user signs out of your application is RP-initiated logout. Back-channel logout is the other direction: SenseCrypt tells you, server-to-server, that a session your application was signed in to has ended — because another application in the same browser signed out, or because something happened to the user.
Register a back-channel logout URL on the application and SenseCrypt POSTs a signed logout_token to it. Discovery advertises backchannel_logout_supported and backchannel_logout_session_supported, both true.
Register the URL
Set backchannel_logout_uri on the OIDC application. It must be an absolute https URL, and its host must not be a private, loopback, link-local or otherwise reserved address — a http URL is refused 422 with code: "url.https_required" and an internal host with code: "url.private_host".
The host is screened again at delivery: the name is re-resolved and the connection is pinned to a validated address, so a name that only resolves inward later is refused then too. Redirects are never followed.
Optionally set backchannel_logout_session_required: true if your implementation needs every token to name a session. SenseCrypt always sends one, so the flag is always satisfied (see The logout token).
Choose the endings you hear about
backchannel_logout_initiators is the list of session endings this application wants a token for. It defaults to ["rp-logout"], so registering a URL does not start a flood of tokens for endings you never asked about.
| Initiator | The ending it names | Opt-in? |
|---|---|---|
rp-logout | A relying party asked — /logout with an id_token_hint, or the confirmed cookie-only logout. | Always on. Registering the URL is the opt-in, so the list must contain it. |
session-revoked | The session ended for a reason other than a lifecycle event: a SCIM active: false suspend, or a different user authenticating in the same browser (an account switch). | Yes |
access-revoked | The user lost access to this application through an authorization change: removed from a group that granted it (console or SCIM), the group deleted, or the group detached from the application. Scoped to the application that lost the user — a member who still reaches other applications keeps the browser session and only the applications they lost are told; a member left in no app-attached group has the session ended and every opted-in application told. | Yes |
account-deleted | The user's account is gone — a user delete from the console or over SCIM. | Yes |
email-identifier-changed | The user's email identifier was changed — from the console or over SCIM — which severs the credentials and sessions bound to the old address. | Yes |
session-expired | The session reached its absolute expiry and was reaped. | Yes |
Send the list on create or PATCH; it is de-duplicated and stored in the order above. A value outside the vocabulary is refused 422 with code: "backchannel_logout_initiators.invalid", and a list that drops rp-logout with code: "backchannel_logout_initiators.requires_rp_logout" — it is refused rather than silently repaired.
idp-logout and password-changed (names you may know from other providers) are not offered: there is no upstream federation on this plane, and no password to change.
What is not announced
- Tenant deletion. It retires the tenant's signing keys and revokes its applications in the same transaction, so no relying party could verify a token afterwards.
- An account holder deleting their own account. That is a reactivatable request that retires nothing.
- Deleting the application. The delete revokes the application in the same transaction, and an announcement only ever goes to a live application — so the logins it had are dropped silently. Note what does not stop the token: the registered
backchannel_logout_urisurvives the delete untouched, so do not assume a delete unregisters your endpoint. Detaching a group from the application is announced; that isaccess-revokedabove.
The logout token
// header
{ "alg": "ES256", "kid": "<tenant key id>", "typ": "logout+jwt" }
// payload
{
"iss": "https://acme.example.com",
"sub": "b1e6…c9",
"aud": "your-client-id",
"iat": 1893455400,
"exp": 1893455520,
"jti": "9f2c…",
"events": { "http://schemas.openid.net/event/backchannel-logout": {} },
"sid": "d4c1…"
}typislogout+jwtand there is nononce. Those two checks are what stop a user's own ID token being posted to your endpoint to sign someone out.- Every token carries
sid— the samesidyour application saw in that session's ID token. SenseCrypt never sends asub-only token: the scope of a token is exactly the scope of what it revoked, which is one session. Do not write a branch that treats a missingsidas "every session for this user". audis yourclient_id;issis the issuer host the session was established on.expis about two minutes afteriat. The token is minted per delivery attempt, so a retry brings a freshiat,expandjti— ajtireplay cache on your side will not reject a legitimate retry.- Signed with the tenant key for your application, using the algorithm you registered as
id_token_signed_response_alg, and read live at delivery — so rotating a key or changing the algorithm takes effect on the next attempt.
RP-initiated logout fans tokens out to every application in that browser session that registered a URL, including the one that asked. Your endpoint will receive a token for a session you have already ended yourself; treat it as a no-op rather than an error.
Delivery
SenseCrypt POSTs to your URL directly, as application/x-www-form-urlencoded:
POST /backchannel-logout HTTP/1.1
Host: yourapp.com
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-store
logout_token=eyJhbGciOiJFUzI1NiIsInR5cCI6ImxvZ291dCtqd3QifQ...200or204means delivered. Any other status — and any transport failure — is a failure, retried with backoff up to 5 attempts in total, after which the notification is abandoned. Unlike the CIBA callback contract, a4xxis not final here: a400is the only way to tell us our token was wrong, so it is retried like any other failure. Answer200for anything you have handled, including a session you no longer know about.- Delivery is queued, not inline. The user's logout redirect never waits on your endpoint, and a slow or dead relying party does not hold up anyone else's token. Expect the POST shortly after the ending, not synchronously with it.
- Redirects are never followed — a
3xxis just a failure.
What your endpoint must check
Validate before you act (OpenID Connect Back-Channel Logout 1.0 §2.6):
- Signature — against the tenant's
jwks_uri, selecting the key by the header'skid. Tolerate more than one published key: a rotation grace window publishes the outgoing key alongside the new one. issequals the tenant issuer you integrated against, andaudequals yourclient_id.iatis recent andexpis in the future. The lifetime is short, so do not queue a token for later processing.eventscontains the memberhttp://schemas.openid.net/event/backchannel-logout.typislogout+jwtand there is nononceclaim. Refuse the token if either is wrong — this is the ID-token-substitution check.sidis present. That is the session to end.- Optionally, refuse a repeated
jtias a replay. Keep the window short: a retried delivery is a newjti, but a genuine replay is not.
Then end the session on your side — delete the session row your store keeps for that sid — and answer:
HTTP/1.1 200 OK
Cache-Control: no-storeOn a token you refuse, answer 400 with a JSON error body. Never answer with a redirect, and never require a browser cookie: this request comes from SenseCrypt's servers, not from the user's browser, so it carries none of the user's cookies and must not be routed through any browser-session middleware or CSRF check.
Related
- Tokens & sessions — the session layers, RP-initiated logout and revocation.
- Refresh tokens and sessions — keeping users signed in, and what a revocation does.
- Relying party — the integration checklist and signing out from your side.
- Account lifecycle — suspend, delete, and what each severs.
- Error codes — the registration rule codes.
Refresh tokens and sessions
Keep users signed in with SenseCrypt refresh tokens — requesting offline_access, handling rotation and reuse detection, revoking sessions, and implementing logout against the OP browser session.
Sender-constrained tokens
Bind access tokens to a key your client holds — DPoP (RFC 9449) proofs and mutual TLS (RFC 8705) certificates, the cnf claim, the DPoP nonce retry, refresh-token binding, and what a resource server must check.