Session vs JWT authentication with revocation service
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Session and JWT authentication both solve the same problem: prove user identity on each request. The real design question is how you want to manage state and revocation under load. If you need immediate logout and strict central control, sessions are usually simpler, while JWT can reduce server coupling when designed with short-lived access and strong refresh-token controls.
Session Authentication: Stateful Control
In a session model, the browser stores only an opaque session identifier, usually in an HTTP-only cookie. The server stores session data in Redis or another shared store, and every request checks that server-side record. Because the server owns state, revocation is straightforward: delete the session key and the user is effectively logged out.
This pattern works well for traditional web apps and internal tools where immediate invalidation matters more than fully stateless APIs. It also makes permission changes easy. If a user loses access to an admin feature, the next request can read updated roles from the session store and enforce the new policy without waiting for token expiration.
JWT Authentication: Stateless Access and Controlled Refresh
JWT shifts request authentication toward self-contained signed tokens. The server verifies the signature and expiration, often without querying session storage. This can simplify horizontal scaling for high-throughput APIs, but revocation becomes harder because a valid signed token remains valid until expiry.
A practical production pattern is:
- Access token with a short lifetime, such as five to fifteen minutes.
- Refresh token stored in a database with device metadata.
- Rotation on every refresh call.
- Revocation by deleting refresh rows and optionally blacklisting current access token identifiers for the remaining access lifetime.
Revocation Service Design
A revocation service closes the gap between stateless verification and real-world security requirements. You can combine three controls to get predictable behavior:
- Refresh-token registry. Keep active refresh tokens in persistent storage with expiry, device name, and last-seen timestamp.
- Token versioning. Store
token_versionon the user record. If you increment it after password reset or account compromise, all previously issued access tokens with old version become invalid. - Access-token denylist for high-risk events. Store
jtivalues in Redis with a TTL equal to remaining access-token lifetime.
This hybrid approach gives you most of the scaling benefits of JWT while keeping emergency lockout and logout behavior deterministic.
Common Pitfalls
- Long-lived access tokens. Fix by using short expiration and rotating refresh tokens.
- Storing tokens in insecure browser storage. Fix by preferring HTTP-only cookies when possible and always using TLS.
- No device-level revocation. Fix by storing refresh token records per device so users can revoke one session without logging out everywhere.
- Missing clock-skew handling. Fix by allowing small leeway during verification and keeping system time synchronized.
- Treating JWT as fully stateless while needing immediate revocation. Fix by adding token version checks and a targeted denylist.
Summary
- Session auth is stateful and easy to revoke immediately.
- JWT can scale well, but revocation requires explicit architecture.
- Short-lived access plus rotated refresh tokens is the most reliable JWT baseline.
- A revocation service should include refresh registry, token versioning, and selective access-token denylisting.
- Choose based on security requirements first, then optimize for operational simplicity.

