On this page
  1. What are the most dangerous JWT mistakes?
  2. How does algorithm confusion actually work?
  3. How should JWTs be stored and used?
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. Is it safe to put user roles in a JWT?
    2. What happens if my JWT secret leaks?
  7. Related topics
  8. Sources
concept

JWT Security: Common Mistakes That Get Tokens Stolen

JWTs are easy to generate and easy to get wrong. Learn the mistakes — algorithm confusion, weak secrets, no expiry — that turn tokens into access.

Quick answer

  • JWTs are signed tokens; the security is entirely in how you create, validate, and store them.
  • The classic failures: no signature verification, algorithm confusion (HS256 vs RS256), weak secrets, and no expiry.
  • Store tokens in httpOnly cookies, not localStorage, and keep lifetimes short.

What are the most dangerous JWT mistakes?

Four stand out. Not verifying the signature at all — trusting the payload of any token sent to you. Algorithm confusion — an attacker sends a token signed with HS256 using the public key as the secret, and a library configured for RS256 accepts it. Weak secrets — HS256 tokens signed with a guessable secret like ‘secret’ are crackable offline. No or long expiry — a stolen token works forever. Each one converts a token you issued into an access key for anyone.

How does algorithm confusion actually work?

RS256 signs with a private key and verifies with a public key. HS256 signs and verifies with the same shared secret. If your server verifies RS256 but doesn’t pin the algorithm, an attacker changes the header to HS256, signs the token with the server’s public key (which is public!), and the server verifies it with that same key as if it were the HS256 secret. The fix: pin the expected algorithm explicitly and reject anything else — most JWT libraries have this option, and it must be on.

How should JWTs be stored and used?

Serve them in httpOnly, Secure, SameSite cookies so JavaScript can’t read them and XSS can’t steal them. localStorage is readable by any script, so a single XSS bug leaks every token. Keep lifetimes short — minutes to hours for access tokens — and support revocation server-side (a blocklist, or short-lived tokens plus refresh flow) because you cannot un-issue a JWT. Validate expiry and issuer on every request, and treat the signing key as a crown jewel: rotate it and never commit it.

# PyJWT: pin the algorithm and verify everything\nimport jwt\n\ntoken = jwt.decode(\n    raw_token,\n    public_key,\n    algorithms=["RS256"],  # pinned — algorithm confusion is rejected\n    issuer="https://auth.example.com",\n    options={"require": ["exp", "iat", "iss"]},\n)

Where this bites vibecoders

Ask an AI assistant for auth and you get a JWT flow in minutes — and usually a textbook example of one of these mistakes: the secret hardcoded as ‘supersecret’, no algorithm pinning, tokens living in localStorage with a 30-day expiry. The token system works in the demo, which is exactly why it’s dangerous. The review checklist for JWT auth is short and specific, and it catches the whole class of ‘auth that authenticates nothing’.

Where AI coding assistants get this wrong

  • Hardcoding the signing secret in the repo instead of an environment variable.
  • Verifying tokens without pinning the algorithm, enabling confusion attacks.
  • Storing tokens in localStorage, exposing them to any XSS.
  • No expiry, or a multi-month expiry, so stolen tokens are keys forever.
  • Trusting the payload (user ID, role) without verifying the signature.

Checklist

  • Verify signature, expiry, issuer, and audience on every request — with the algorithm pinned.
  • Use RS256 (or better) with a private key that’s never committed.
  • Serve tokens in httpOnly Secure SameSite cookies; avoid localStorage.
  • Keep access tokens short-lived and support revocation.

FAQ

Is it safe to put user roles in a JWT?

Yes, if the token is properly signed and verified — the payload is tamper-evident. The danger is trusting role claims without signature verification, or relying on roles in the token when they’ve changed server-side since issuance (a demoted user keeps their old role until expiry). For sensitive decisions, check the current role server-side.

What happens if my JWT secret leaks?

Anyone with the secret can forge tokens for any user. Rotate the secret immediately — which invalidates all existing tokens — and check for signs of forged tokens in logs. This is why the secret belongs in a secrets manager, rotated on a schedule, never in the repo.

Sources

Share: