We use analytics cookies.We use cookies to analyze site traffic and improve your experience.
Loading...
This idea carries a full system design question on its own. Each walks through the full answer.
System Design Masterclass · Security Architecture
Free preview: the first 2 parts of this 8-part lesson.
Already bought the course? Sign in to read the whole lesson.
this lesson is part of
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design courseread the rest of this lesson in
System Design Masterclass
Unlock this lesson and the 751 other paid lessons
$25 once, by PayPal
See the System Design coursein Security Architecture
This lesson is part of the full System Design Masterclass. Unlock it along with 769 other lessons for a one-time payment.
One-time payment by card or PayPal
Get Lifetime AccessOne-time payment: UPI, card or netbanking
Get Lifetime Access18 Foundation lessons are free, no payment needed. Try them now
It is easy and good
“It is really nice and good Please join the course”
Using JWTs as session tokens sounds clean on paper. Stateless. No . No database lookups for authentication. Every server verifies locally. Horizontal scaling is free.
Then a user gets compromised. You need to kill their session RIGHT NOW. With server-side sessions, you delete the key from Redis and the session is dead in milliseconds. With sessions, the token is valid until it expires. You issued it with a 1-hour expiry. The attacker has 59 minutes of access.
The workaround is a blocklist: a server-side list of revoked JWT IDs. But now you have server-side state again. Every request must check the blocklist. You need Redis again. The stateless promise collapses.
This is the core tension of JWT sessions, and the industry has spent a decade trying to resolve it. The as of 2026: use short-lived JWTs (15 minutes) as access tokens combined with server-side refresh tokens. You get stateless verification for the vast majority of requests while maintaining revocation capability through the refresh token.
Auth0, Firebase, and Supabase all follow this pattern. Okta documents it as a best practice. The IETF's 2.0 Security Best Current Practice (RFC 9700) recommends it explicitly.
The access + refresh token pattern:
| Token | Format | Lifetime | Storage | Server-side state |
|---|---|---|---|---|
| Access token | (signed, self-contained) | 15 minutes | In-memory (JS variable) or HttpOnly cookie | None (verified by signature) |
| Refresh token | Opaque string (random bytes) | 7-30 days | HttpOnly cookie (web) or secure enclave (mobile) | Yes (stored in database) |
The flow:
Why 15 minutes? Short enough that a stolen access token has limited utility. Long enough that users are not refreshing every 30 seconds. Stripe and Auth0 both use 15-minute access tokens. Some high-security environments (banking) use 5 minutes.
Why server-side refresh tokens? The refresh token is the revocation handle. When a user logs out, you delete the refresh token from the database. When a user changes their password, you delete all their refresh tokens. When you detect suspicious activity, you revoke the token family. The 15-minute window of the access token is the maximum exposure.