Aller au contenu principal

Active Sessions & Remote Revoke

Every successful SyAuth login (password, magic-link, or federated) now creates an ExtsUserSession row keyed on the refresh-token jti. End users can list every device signed into their account and revoke any of them on demand — the underlying tokens are pushed to the persistent TokenDenylist, so revocation survives Redis restarts.


End-user UI

A Active Sessions page is included in the SyAuth dashboard (/security/sessions). It shows:

  • Device name (if known) and user agent
  • IP address and last-seen timestamp
  • Whether the session is currently active, revoked, or is the session making the call ("This device")
  • Per-session Revoke button
  • A Sign out of all other sessions button — revokes everything except the current session

API reference

All endpoints require Authorization: Bearer <access_token>. Every revoke also emits an immutable audit_event (session_revoke / session_revoke_all).

GET /user/sessions/

List every session (active + recently revoked) for the authenticated user.

{
"results": [
{
"id": "6f...",
"device_name": "",
"user_agent": "Mozilla/5.0 ...",
"ip_address": "203.0.113.42",
"created_at": "2026-04-17T10:00:00Z",
"last_seen_at": "2026-04-17T11:00:00Z",
"expires_at": "2026-04-24T10:00:00Z",
"revoked_at": null,
"revoked_reason": "",
"is_active": true,
"is_current": true
}
]
}

DELETE /user/sessions/{session_id}/

Revoke a specific session. Both the refresh-token jti and the captured access-token jti are added to TokenDenylist. Returns 204 No Content.

DELETE /user/sessions/

Revoke every active session for the user except the one making the call ("sign out of all other devices").

{ "revoked": 4 }

How revocation works

  1. On successful login, SyAuth creates an ExtsUserSession(user, refresh_jti, access_jti, …).
  2. When the session is revoked:
    • ExtsUserSession.revoked_at is set.
    • Both jti values are inserted into TokenDenylist with reason user_logout_all.
  3. On every authenticated request, SyAuth's access-token middleware already consults TokenDenylist. A revoked jti means the token is rejected immediately — even if the JWT itself is still syntactically valid.

Because the denylist is persistent, revocations survive Redis restarts; because it's keyed on jti, it works cleanly across all our existing JWT flows.


Audit trail

Every revocation is logged to the Audit Log:

EventWhen
session_createAny time a new session row is created (login + magic-link + MFA verification).
session_revokeA single session is revoked from the UI or API.
session_revoke_all"Sign out of all other sessions" is used; metadata.revoked_count records the total.

Operational notes

  • New devices land without a name. device_name is a client-supplied label; populate it from your own UX if you want "MacBook Pro" rather than a blank cell.
  • Expired sessions are not auto-purged. They stay in the ledger for auditability. Add a nightly cleanup job if you want to trim.
  • OAuth-client context is retained. If the session was initiated through a specific OAuth client, that's stored on the row so developers can answer "who signed in from which app?" later.
  • Mobile apps should call DELETE /user/sessions/ on password change. We do not force this server-side yet — wire it into your client to match best practice.