ADR-0003. Stateless JWT — no revocation list, short TTL + refresh
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-02-10 |
| Deciders | identity-team |
| Supersedes | — |
Context
- JWTs are signed claims; once issued, they're valid until expiry — there's no way to instantly invalidate them without state.
- Adding a revocation list (blocklist) means every sister service must check it on every request — defeats the point of stateless verification.
- POS workflows need responsive logout / lockout (admin disables a user → they should be cut off quickly).
Decision
JWTs are stateless — no revocation list. We accept the trade-off:
- TTL is short (default 1 hour) — bounded blast radius.
- Refresh tokens are rotated on use — long-lived sessions still get fresh tokens.
- Lockout / disable updates
User.statusimmediately, but token holders retain access until natural expiry. Critical lockouts (security incidents) require key rotation.
Consequences
| Pros | Cons |
|---|---|
| Sister services verify locally without any shared state | Disabled users keep access for up to TTL |
| No central blocklist to consult | Critical lockout requires JWKS key rotation (force-logs-out everyone) |
| Horizontal scale is trivial | "Logout other sessions" is best-effort UX (token in browser still valid) |
Mitigation patterns
- Sensitive operations (password change, role downgrade) re-fetch user state and re-evaluate authorization server-side
- Short TTL (1h) + refresh on activity = 1h max stale window
- For incidents: rotate JWKS keypair → all tokens become invalid
Alternatives Considered
| Option | Pros | Cons | Why rejected |
|---|---|---|---|
| Redis-backed token blocklist | Instant revocation | Sister services need to check Redis on every request — defeats stateless verification | Wrong layer |
| Reference tokens (opaque, server-stored) | Instant revocation | Every request → identity HTTP call; latency disaster | Wrong tradeoff |
| Very-short TTL (5min) | Smaller stale window | Excessive refresh churn | Perf cost |
References
JWKSIssuerTokenService(TTL configuration)AuthenticationService.refreshToken