2 min lesson
JWT failure modes to name unprompted
For each case in "JWT failure modes to name unprompted", name the signal and the response you would use.
Step 1 of 2
JWT failure modes to name unpromptedthe security-credibility checklist
A token claims it's unsigned and a naive verifier accepts it.
Pin the expected algorithm server-side; never trust the token's own alg header.
RS256 token re-signed as HS256 using the public key as the HMAC secret.
Same fix: hard-code the accepted alg, separate signing from verification keys.
A token minted for service A is replayed against service B.
Validate issuer and audience on every verify, not just the signature.
Stuffing every permission into the JWT bloats every request header.
Keep claims minimal; resolve fine-grained authz server-side.
Two more that bite at scale: tokens that never set exp and signing keys that are short, hand-rolled or shared across environments. A leaked HS256 secret lets anyone forge tokens for everyone, which is why asymmetric RS256/ES256 with a private signing key is the default at a 1M-user tool.
"I'd use short-lived access JWTs for the hot path so most requests verify locally and stay cheap, with refresh tokens and a token-version check for revocation. The cost is a bounded staleness window - about one access-token TTL - and I'd keep that under fifteen minutes so a compromised account can't stay live for long."
At 1M+ DAU, pure server-side sessions put a store lookup on every request, so that store becomes a single point of failure for the whole product. If Redis hiccups, nobody can do anything. Stateless JWT verification degrades gracefully because it needs no external call to authenticate - that's a reliability argument, not just a performance one and it's the framing Cursor wants from a Core Services EM.