Skip to main content

JWT Session Management

Relay stores JWT sessions in the cache backend (Redis in production, in-memory for development) so tokens can be revoked across the cluster in near real time.

Session Registry

Sessions persist through src/services/session_store.py and include:

  • session_id, user_id, and timestamps (created_at, last_active_at, expires_at)
  • network context (ip_address, user_agent)
  • token catalogue with per-token JTI, type, issued time, and expiry
  • revocation metadata (revoked, revoked_at, revoke_reason)

The store maintains a user → sessions index (user-sessions:{user_id}) so that all sessions for a user can be revoked atomically. TTL derives from the refresh expiry (minimum 60 seconds) and records update asynchronously whenever JWTs are issued or validated.

Runtime Integration

SecureJWTAuth wires persistence hooks whenever access/refresh tokens are created or validated. Revoking a session updates both the in-memory tracker and the persistent store. The API dependency get_current_user_from_token checks the store on every request to honour revocations across nodes.

Key entry points:

  • src/security/jwt_auth.py — session persistence hooks
  • src/api/routes/jwt_auth.py — login/refresh/logout wiring and session APIs
  • src/services/session_store.py — cache-backed session registry

API Surface

New endpoints under /auth expose the persisted state:

  • GET /auth/sessions — list active sessions for the authenticated user
  • DELETE /auth/sessions/{session_id} — revoke a specific session
  • POST /auth/logout — supports revoke_all_sessions and persists revocation to the registry

Example response:

[
{
"session_id": "b7…",
"created_at": "2025-09-21T19:22:00+00:00",
"last_active_at": "2025-09-21T19:28:12+00:00",
"expires_at": "2025-09-22T19:22:00+00:00",
"revoked": false,
"ip_address": "203.0.113.10",
"user_agent": "Mozilla/5.0",
"tokens": [
{"token_jti": "a1…", "token_type": "access"},
{"token_jti": "d4…", "token_type": "refresh"}
]
}
]

Operational Notes

  • Cache TTL — A session expires when the associated refresh token expires; the cache key inherits that TTL.
  • Revocation propagation — Calls to revoke_session, revoke_user_sessions, or the logout endpoint mark records as revoked and prune the user index. get_current_user_from_token denies future requests against revoked sessions immediately.
  • Audit support — Combine this guide with /auth/sessions exports to satisfy zero-trust audit requirements.

Verification

Run the integration suite to confirm the behaviour end-to-end:

pytest tests/integration/test_auth_session_revocation.py

For a lighter smoke run:

python3 scripts/monitoring/validate_monitoring_stack.py

Both commands ensure the session registry is wired correctly and that the persisted metadata survives logout flows.