Sessions vs JWTs
Two ways to remember who's calling, with genuinely different failure modes — especially logout.
50 minDifficulty 3/5auth · securityAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.
Before this
Why this exists
HTTP is stateless, but user workflows require identity across requests. Early web architectures stored stateful session IDs on the server backed by Redis or memory, but distributed microservices struggled with centralized session lookups. Teams adopted stateless JSON Web Tokens (JWTs) to eliminate database reads on every request, inadvertently trading away instant revocation and tight security boundaries.
The mental model
A **Session** is a coat check ticket: a meaningless random token that points to your physical coat stored securely in the cloakroom. A **JWT** is a stamped passport: you carry all your credentials inside it, and any guard can verify the stamp cryptographically without checking a central registry, but if your passport is stolen, you cannot easily invalidate it until it expires.
How it works
Stateful Sessions Trade Storage for Control
A session uses an opaque reference identifier stored in a cookie. The server keeps session metadata (user ID, permissions, expiry) in a fast key-value store like Redis. When a user logs out, changes roles, or gets banned, the server deletes the Redis entry, instantly revoking access across all downstream requests.
Stateless JWTs Trade Revocation for Scale
A JWT packages claims (`sub`, `exp`, roles) into a base64-encoded payload signed cryptographically with a secret (`HMAC-SHA256`) or private key (`RS256`). Microservices verify the signature locally using the public key or shared secret without hitting a database. However, once issued, a JWT is valid until its expiration timestamp (`exp`).
Solving Revocation Turns JWTs Back Into Sessions
To force-logout a compromised JWT or handle immediate permission downgrades, teams often implement a token blocklist in Redis. Once every microservice must query Redis to check if a JWT is blocklisted, the stateless latency advantage disappears, leaving behind all the payload size and serialization overhead of tokens with none of the stateless benefits.
The Short-Lived Access and Long-Lived Refresh Pattern
Modern authentication balances this by pairing an ultra-short-lived access token (e.g., JWT expiring in 5–15 minutes) with a stateful, revocable refresh token stored in an `HttpOnly` cookie. The access token hits microservices fast and stateless; the refresh endpoint checks the database only every few minutes to issue fresh access tokens.
The mechanism
In a session flow, the client sends a cookie containing `session_id=xyz`. The server queries Redis via `GET session:xyz` to retrieve identity and permissions. In a JWT flow, the client sends `Authorization: Bearer <token>`. The server parses header and payload, verifies the signature against its public key, checks that `exp > now()`, and parses the identity directly from payload claims without any network I/O.
sequenceDiagram
autonumber
participant Client
participant API as API Service
participant DB as Redis / Auth DB
Note over Client,DB: Session Architecture
Client->>API: GET /resource (Cookie: sid=abc)
API->>DB: GET session:abc
DB-->>API: { user_id: 42, role: "admin" }
API-->>Client: 200 OK
Note over Client,DB: Stateless JWT Architecture
Client->>API: GET /resource (Bearer <jwt>)
API->>API: Verify signature + exp locally
API-->>Client: 200 OK (No DB read)What people get wrong
- JWTs are inherently more secure than session cookies.
- JWT signatures guarantee data integrity, not security against theft or leakage. Storing JWTs in browser localStorage exposes them directly to Cross-Site Scripting (XSS) attacks. Session cookies using HttpOnly and SameSite flags are immune to script-based exfiltration.
- JWT payload data is encrypted and hidden from users.
- Standard JWTs (JWS) are merely base64URL encoded and signed, not encrypted. Anyone with access to the token string can decode the payload and read all contained claims, making it unsafe for secrets or sensitive personal identifiable information without nested encryption (JWE).
When not to use it
- Monolithic architectures or applications with strict compliance requirements for immediate access revocation (e.g., banking, admin control panels).
- Stateful sessions backed by Redis or an in-memory session store.
- Systems passing large amounts of user claims or permissions across many internal microservices.
- Opaque reference tokens at the edge gateway that resolve to internal context headers, avoiding large JWT HTTP headers.
Terms
- Opaque Token
- — A random string with no internal meaning or readable user data, requiring a server-side lookup to resolve identity.
- Claims
- — Pieces of information asserted about a user (such as user ID or role) encoded directly inside a token payload.
- Revocation Denylist
- — A database cache of revoked token identifiers checked during validation to reject tokens before their natural expiration.
In an interview
How do you implement immediate user logout or revocation when using JWTs?
- Short token lifetimes (5-15 mins) combined with stateful refresh tokens
- Redis-backed token blocklist (denylist) storing revoked token IDs with TTL equal to remaining expiry
- Trade-off acknowledgment: adding a denylist reintroduces stateful DB checks to a stateless system
Where should you store authentication tokens in a single-page application?
- HttpOnly, Secure, SameSite cookies to protect against XSS token exfiltration
- Why localStorage is vulnerable to malicious scripts or compromised npm packages
- Handling CSRF protection when using cookie-based token delivery
Can you recall it?
What is the fundamental trade-off between server-side sessions and stateless JWTs regarding database load and revocation?
Connected ideas
- OAuth 2 & OpenID Connect — Delegated access done right — authorization vs authentication, and why the difference matters.
- Storing Passwords — bcrypt/argon2, salts, and why 'we hash with SHA-256' is a breach report waiting to happen.
- Caching with Redis — An in-memory store used as cache, session bag, lock, counter, and queue.
Also part of
This idea matters in more than one area — which is usually why it matters.