Guides

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.

Last updated 2026-06-20

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:

StatusMeaning
activeNormal — the session can refresh tokens
suspendedTemporarily frozen; reactivatable
revokedTerminated by you or the user — terminal
expiredAged 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

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

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:

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: 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 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 sessionsrevoke_all_user_sessions: true with reason security_event. Kills refresh immediately, triggers backchannel logout.
  2. Block the userPATCH .../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 devicesPOST .../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 referencesid, token TTLs, and what’s inside the tokens sessions issue
  • Organization lifecycle — the same bounded-staleness model applied to membership changes
  • Webhooks — consuming session.terminated and session.compromised
  • API keys — the credential you’re calling this API with