JWT vs Session Auth: Picking the Right Model
Statelessness sounds appealing until you need to revoke a token. A clear-eyed look at the trade-offs between JWTs and server-side sessions.
The debate between JWTs and session-based auth generates more heat than it should. Neither is universally better. The right choice depends on what you're building, who's consuming it, and what your revocation requirements look like.
How Sessions Work
The server stores session state (user ID, roles, expiry) in a store — Redis, database, or memory. It issues the client an opaque session ID, typically in an httpOnly cookie. On each request, the server looks up the session ID in the store.
Pros: Instant revocation (delete the session row). Payload never exposed to the client. Trivial to inspect all active sessions.
Cons: Every request hits the session store. Requires sticky sessions or a shared store in distributed deployments. Doesn't work cleanly for non-browser clients that don't handle cookies.
How JWTs Work
The server issues a signed token containing the claims directly (user ID, roles, expiry). The client stores it and sends it with every request. The server validates the signature and reads the claims — no store lookup needed.
Header.Payload.Signature
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMiLCJleHAiOjE3MDB9.abc123
Pros: Stateless — horizontally scalable with no shared store. Works naturally for APIs consumed by mobile apps, SDKs, cross-origin clients.
Cons: Revocation is hard. A signed JWT is valid until expiry. If a user logs out or is compromised, the token keeps working until it expires. The only practical mitigation is short expiry plus a refresh token.
The Revocation Problem
This is the crux. Session auth wins unambiguously on revocation. You delete one row and the session is dead immediately.
With JWTs, your options are:
- Short expiry (5–15 min) + refresh token rotation. The access token can't be revoked, but it expires fast. The refresh token can be rotated and invalidated.
- Blocklist. Maintain a Redis set of revoked JWTs until their natural expiry. You've re-introduced state — but only for the exception case.
- Accept the risk. For low-sensitivity apps where a 15-minute window of residual access is tolerable, option 1 alone is fine.
Storage Risks
Where the client stores the token matters enormously.
localStorage is readable by any JavaScript on the page. An XSS vulnerability anywhere on your domain exposes every token in storage.
httpOnly cookies are not readable by JavaScript. They're immune to XSS but require CSRF protection (SameSite=Strict or CSRF tokens).
Better-Auth (used by NodWatch) stores sessions in httpOnly cookies with SameSite=Lax by default and handles CSRF automatically. This is the correct default for web apps.
For native mobile apps or CLIs, localStorage-equivalent storage is often unavoidable — short expiry plus refresh rotation is the mitigation.
Practical Decision Guide
| Use case | Recommendation | | --- | --- | | Web app, same-origin API | Sessions in httpOnly cookies | | API consumed by mobile / third parties | JWTs with short expiry + refresh rotation | | Microservices (service-to-service) | Short-lived JWTs, no refresh needed | | Anything requiring instant revocation | Sessions, or JWT + blocklist |
Claim Bloat
JWTs are often misused as a data transport layer. Every extra claim added to the token gets sent with every request. Stick to the minimum: user ID, roles, expiry, and token type. Fetch everything else from a cache or DB when needed.
Algorithm Choice
Always use RS256 (asymmetric) when multiple services need to verify tokens independently — they can verify with the public key without needing the signing secret. Use HS256 (symmetric) only when a single service signs and verifies.
Never use none algorithm. Some libraries accept it by default. Explicitly specify the expected algorithm on the verification side.
jwt.verify(token, publicKey, { algorithms: ['RS256'] });The choice between JWT and sessions isn't about which is "better." It's about whether your architecture tolerates statelessness and what your revocation requirements dictate.