> Markdown version of https://authpi.com/docs/guides/session-management/ — fetch the complete AuthPI docs index at https://authpi.com/llms.txt to discover all available pages.

# Manage and revoke sessions

List, revoke, and suspend user sessions on AuthPI — log a user out everywhere, understand what revocation does to tokens, and the full kill-switch sequence for compromised accounts.

Every login creates a session on AuthPI, and sessions are what you act on when someone reports a stolen laptop or you offboard an employee. This guide covers the session API — listing, revoking, suspending — and, just as importantly, exactly what each action does and doesn't do to the user's tokens.

## What a session is

A session ties together one authentication event: who logged in (`user_id`), how (`authentication.method`), from where (`device`, `location`), through which OAuth client, and which tokens it has issued (`refresh_count`, token issuance timestamps — JTIs are tracked, token values are never stored). What the API returns is a public projection: the cookie-verification secret AuthPI holds internally is never included in session responses. Sessions move through a small lifecycle:

| Status | Meaning |
|--------|---------|
| `active` | Normal — the session can refresh tokens |
| `suspended` | Temporarily frozen; reactivatable |
| `revoked` | Terminated by you or the user — terminal |
| `expired` | Aged out — terminal |

Defaults: sessions live **7 days**, idle-expire after **12 hours** without activity, and each user holds at most **50 sessions** — creating a 51st auto-expires the oldest. Both lifetimes are configurable per issuer (`settings.access.sessions.max_age` / `idle_timeout`, in seconds, capped at 1 year and 30 days respectively); a session past either limit can no longer refresh — absolute expiry applies even if the session is still inside its idle window.

AuthPI also distinguishes session `type` in API responses. An `op` session is the SSO anchor held by AuthPI; an `rp` session belongs to a specific OAuth client and links back to that OP session. Revoking an OP session also revokes its linked RP sessions for the same user and issuer, so an SSO logout can fan out to every client session created from that browser login.

## List a user's sessions

```bash
curl "https://api.authpi.com/v1/accounts/acc_7k2m9x4p1q8w5e3r6t0y2u4i8/issuers/i_4r8w2k9m5x1p7q3e6t0y2u4i8/users/usr_8t2y6u4i0o5p3a7s1d9f2g4h6/sessions?status=active" \
  -u "$AUTHPI_KEY_ID:$AUTHPI_KEY_SECRET"
```

Each record carries the device and location captured at login — enough to build a "your active sessions" page where users spot the login that isn't theirs.

## Revoke a session

```bash
curl -X POST "https://api.authpi.com/v1/accounts/acc_7k2m9x4p1q8w5e3r6t0y2u4i8/issuers/i_4r8w2k9m5x1p7q3e6t0y2u4i8/sessions/ses_3f7a1k9m2x5p8q4e6t0y2u4i7/revoke" \
  -u "$AUTHPI_KEY_ID:$AUTHPI_KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "security_event" }'
```

Valid `reason` values: `user_logout`, `admin_action`, `security_event`, `password_changed`, `inactivity`, `token_compromised`, `other`. Revocation is idempotent and terminal.

### Log a user out everywhere

There is no separate "revoke all" endpoint — pass `revoke_all_user_sessions: true` on a revoke of **any one** of the user's sessions (take an id from the list call), and every non-terminal session of that user is revoked. That includes active sessions, suspended sessions, and brief inactive sessions still between authorization and token issuance. If any target is an OP session, its linked RP sessions are included too:

```bash
curl -X POST ".../sessions/ses_3f7a1k9m2x5p8q4e6t0y2u4i7/revoke" \
  -u "$AUTHPI_KEY_ID:$AUTHPI_KEY_SECRET" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "security_event", "revoke_all_user_sessions": true }'
```

## What revocation actually does

Three different things happen to the three kinds of credentials, and the differences matter:

- **Refresh tokens die immediately.** The session's tokens are marked revoked the moment you call revoke; the next refresh attempt fails with a session-expired error. The user cannot mint new access tokens.
- **Already-issued access tokens live until they expire.** Access tokens are stateless JWTs — revoking the session doesn't reach into tokens already in the wild. The exposure window is the access-token TTL, **30 minutes by default** and configurable per client. This is the same bounded-staleness model as [organization changes](/docs/guides/org-lifecycle): shorten the TTL where you need a tighter bound.
- **OIDC clients get a backchannel logout.** If an affected session belongs to a client with a `backchannel_logout_uri` configured, AuthPI POSTs a signed OIDC `logout_token` (with the session's `sid`) to it, retrying up to five times. Handle it server-side to clear your own application session immediately instead of waiting out the access-token TTL. Sessions whose client has no backchannel URI get no notification — configure one for every client that maintains its own session state.

A `session.terminated` event also fires per revoked session — a single OP logout or "revoke all" call can therefore produce multiple events with the same revocation reason. Subscribe via [webhooks](/docs/guides/webhooks) for audit trails or downstream cleanup. (Suspending or reactivating emits `session.updated`.)

## Suspend instead of revoke

Suspension freezes a session reversibly — refresh fails while suspended, but `POST .../sessions/{id}/reactivate` restores it. Use it when you need to pause access during an investigation without forcing the user through login again. `suspend_all_user_sessions: true` works like its revoke counterpart.

## Built-in protections you get for free

Two session-killing behaviors run automatically, no API call needed:

- **Refresh-token replay detection.** Refresh tokens rotate on every use, and only the newest one is valid. If anyone presents a *previous* refresh token — the classic sign of a stolen token being replayed — AuthPI doesn't just reject it: it marks the whole session compromised, revokes it, and fires a `session.compromised` event.
- **Device fingerprint validation** (opt-in, per client): when `require_device_fingerprint_on_refresh` is set in a client's session settings, a refresh arriving from a mismatched device kills the session the same way. Leave this **off for confidential server-side clients** — the server's user agent never matches the browser's, and every session would self-destruct on first refresh. It's meant for public clients that refresh from the browser.

## The kill-switch: cutting off a compromised user completely

Revoking sessions alone is not the whole job — a user can hold credentials that aren't session-bound. The full sequence:

1. **Revoke all sessions** — `revoke_all_user_sessions: true` with reason `security_event`. Kills refresh immediately, triggers backchannel logout.
2. **Block the user** — `PATCH .../users/{user_id}` with `status: "blocked"`. Blocking prevents new logins *and* stops token refresh: the refresh endpoint rejects a blocked (or suspended, or deleted) user with `invalid_grant`, so blocking alone caps the user's access at the current access-token TTL. Pair it with step 1 anyway — revocation terminates the existing sessions outright and triggers backchannel logout, and access tokens already in the wild ride out their TTL either way.
3. **Revoke personal access tokens** — list the user's `ptk_` tokens and revoke each (`POST .../tokens/{token_id}/revoke`). These are independent of sessions and survive session revocation.
4. **Revoke trusted devices** — `POST .../users/{user_id}/trusted-devices/revoke-all`. Trusted devices are an MFA-skip list, not sessions — clearing them forces full MFA on the next login attempt.

After step 1, the residual exposure is in-flight access tokens (≤ the access-token TTL); backchannel logout closes even that gap for clients that implement it.

## Next steps

- [Token claims reference](/docs/reference/token-claims) — `sid`, token TTLs, and what's inside the tokens sessions issue
- [Organization lifecycle](/docs/guides/org-lifecycle) — the same bounded-staleness model applied to membership changes
- [Webhooks](/docs/guides/webhooks) — consuming `session.terminated` and `session.compromised`
- [API keys](/docs/guides/api-keys) — the credential you're calling this API with