5 ways developers get JWT authentication wrong
JWT authentication is one of those things that looks simple and has a lot of ways to go wrong subtly.
1. Using 'none' as the algorithm
The JWT spec includes a none algorithm, meaning no signature. Early JWT libraries would accept this as valid. If your library doesn't explicitly reject unsigned tokens, an attacker can forge any token by setting alg: none.
Always pin the expected algorithm on verification. Never accept what the token header claims.
2. Storing tokens in localStorage
localStorage is accessible to any JavaScript running on the page. If you have an XSS vulnerability, the attacker can read every token in storage. HttpOnly cookies aren't accessible to JavaScript — that's the point.
3. No token expiry
A JWT with no exp claim is valid forever. If a token leaks — via a log, a screenshot, a compromised device — it stays valid indefinitely. Short-lived access tokens (15 minutes) plus refresh tokens are the standard pattern.
4. Not verifying the signature
Sounds obvious. Some developers decode the JWT payload (base64 decode, no verification) and use the claims directly without verifying the signature. The payload can contain anything. You need the signature check to trust the claims.
5. Putting sensitive data in the payload
JWT payloads are base64-encoded, not encrypted. Anyone who has the token can decode and read the payload. Don't put anything sensitive (passwords, full PII, internal user flags) in the claims.