ML
Security

JWT in 2026: When It's Right, When It's a Footgun

JWTs are not a session system. They are a signed claim. Confusing the two is how teams ship subtle auth bugs.

December 12, 20259 min readSecurityAuth

Every few months I review a service whose auth design is "JWT in localStorage, valid for a week, no revocation." It mostly works. Until it doesn't, and the postmortem is the same one every time. JWTs are not the problem — using them as a session store is.

1. What a JWT actually is

Three base64url segments joined by dots: header, payload, signature. The header says how the signature was computed; the payload is a JSON blob of claims; the signature is the proof that the issuer signed it.

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzQyIiwiZXhwIjoxNzM4...
<rsa signature bytes>

The signature lets you verify the payload was issued by the holder of the key. That is all it does.

2. The fundamental property: you cannot un-issue one

Once a JWT is signed and handed to a client, every server that knows the key will accept it until exp passes. There is no central session store to delete from. That is the feature — verification needs only the public key, not a database — and the trap.

If you need to log a user out, ban an account, or rotate compromised credentials, you cannot, unless you also maintain a revocation list. Which is a session store. Which is what you were trying to avoid.

3. The four wrong shapes

3.1 Long-lived JWT as a session

The classic. A 7-day token in localStorage, no refresh, no revocation. Steal the token → impersonate the user for a week. Don't do this.

3.2 JWT signed with HS256 + shared secret across services

Symmetric signing means every verifier also has the keys to sign. One compromised service issues tokens that all the others accept. Use RS256 / EdDSA where the signer holds a private key and verifiers hold only the public one.

3.3 The none algorithm

The JWT spec accepts alg: none as "no signature." Some libraries used to honour this on verify. Today most don't, but pin the expected algorithm explicitly in your verifier — never let the token tell you how to verify itself.

3.4 Trusting the kid header without validation

The kid (key ID) header tells you which key to verify with. Many libs lookup by it. If the value is unvalidated, an attacker points it at an arbitrary key — sometimes a URL, sometimes a path on disk. Whitelist allowed key IDs.

4. The shape that actually works

  • Short-lived access token — 5–15 minutes. JWT, used by every service.
  • Long-lived refresh token — opaque (NOT a JWT), stored server-side, revocable. Used only at /auth/refresh.
  • Refresh rotation — every refresh issues a new refresh token and invalidates the old one. Detect re-use as a theft signal.
  • HttpOnly cookies for the refresh token. JS cannot read it; XSS can't exfiltrate it.

The access token can be in memory or in a non-HttpOnly cookie. The refresh token must not be reachable by client JavaScript.

5. Claims you should always put in the payload

  • iss — who issued the token. Verifiers MUST check.
  • aud — who the token is for. Prevents a token for service A being replayed at service B.
  • exp — expiry. Short.
  • iat — issued-at. Lets you reject any token issued before a "all sessions invalid before X" timestamp per user.
  • jti — JWT ID. Required if you want to deny-list individual tokens.

6. When NOT to use JWT

  • First-party web apps with a session backend — use server-side opaque sessions. They are simpler, revocable, and don't leak claims.
  • Anywhere "logout" needs to be immediate.
  • Anywhere the token would be exposed to JavaScript in localStorage.

Rules of thumb

  • JWT is for service-to-service trust and short-lived API access — not for session management.
  • Always pair short-lived JWT with a longer-lived, server-revocable refresh.
  • Always pin algorithms and validate iss + aud explicitly.
SharePostLinkedIn

Reader Discussion

1 replies// weighed in

TopNewestAuthor
Add to the thread
Disagree, agree harder, or share your own experience…
Email instead →markdown okbe kind
  1. Isabella Costa· Junior EngineerKind words

    saved this. sharing at standup tomorrow — we've had exactly this problem for 2 sprints and nobody on the team had framed it this way 🙏

    Dec 14, 2025·2 days later

Worked on something similar? Email ducminhldm@gmail.com — I read every one. The good ones become future posts.

Comments seeded · live discussion via email