---
title: "End the session when the person signs out · Agent Wranglers"
url: https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/back-channel-logout
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# End the session when the person signs out at the provider

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Business sign-on and provisioning](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: ACC-68
- Level: Recommended
- Chapter: [Business sign-on and provisioning](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning)
- Rule: For single sign-on through OpenID Connect, register a back-channel logout address. End the matching sessions when a valid logout token arrives.
- Applies when: The project has user accounts.
- How: OWASP ASVS 5.0 requirement 7.1.3 asks that logouts line up across every system in a single sign-on setup. OpenID Connect Back-Channel Logout 1.0 lets the provider post a signed logout token straight to your server. Store the provider's `sid` and `sub` with each session. Verify the token as [verify every token](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/token-checks) says, with `iss`, `aud`, `iat` and `exp`. Require the `events` claim with the back-channel logout member and at least one of `sid` or `sub`. Refuse a token that holds a `nonce` or a `jti` seen before. Answer 200 with `Cache-Control: no-store` after deleting the sessions, and 400 when any check fails. SAML providers use single logout instead. Account-wide events come through [shared signals](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/shared-signals).
- Check: A test provider posts a valid logout token for a signed-in test user. The user's next request answers 401. Tokens with a `nonce`, without `events` or with a wrong `aud` each get 400, and the session stays.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { jwtVerify } from 'jose';

const LOGOUT_EVENT = 'http://schemas.openid.net/event/backchannel-logout';

export async function handleLogoutToken(db, token, provider) {
  const { payload } = await jwtVerify(token, provider.keys, { issuer: provider.issuer, audience: provider.clientId, algorithms: ['RS256', 'ES256'] });
  const isLogoutToken = payload.events?.[LOGOUT_EVENT] && (payload.sid || payload.sub) && !('nonce' in payload);
  if (!isLogoutToken || !(await db.seenTokenIds.addOnce(payload.jti))) throw new BadRequestError('invalid logout token');
  await db.none('DELETE FROM sessions WHERE issuer = $1 AND (provider_sid = $2 OR provider_sub = $3)', [payload.iss, payload.sid ?? null, payload.sub ?? null]);
}
```

### Python example

```python
import jwt

LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout"

def handle_logout_token(db, token: str, provider) -> None:
    claims = jwt.decode(token, provider.signing_key(token), algorithms=["RS256", "ES256"],
                        issuer=provider.issuer, audience=provider.client_id, options={"require": ["iat", "exp", "jti"]})
    is_logout_token = LOGOUT_EVENT in claims.get("events", {}) and (claims.get("sid") or claims.get("sub")) and "nonce" not in claims
    if not is_logout_token or not db.seen_token_ids.add_once(claims["jti"]):
        raise BadRequest("invalid logout token")
    db.execute("DELETE FROM sessions WHERE issuer = %s AND (provider_sid = %s OR provider_sub = %s)",
               (claims["iss"], claims.get("sid"), claims.get("sub")))
```

## Sources

- [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html)
- [OWASP ASVS 5.0 V7 Session Management](https://github.com/OWASP/ASVS/blob/master/5.0/en/0x16-V7-Session-Management.md)

## Related rules

- [Verify every token](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/token-checks) in Security testing
- [Listen for security signals from the customer's provider](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/shared-signals) in Accounts

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/back-channel-logout">Agent Wranglers accounts playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Ask the provider for a fresh sign-in before sensitive changes](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/sso-reauthentication)
- [Business sign-on and provisioning](https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning)
