JWT common security mistakes
Learn the JWT mistakes that cause real authentication bugs: alg none, missing exp, long-lived tokens, sensitive payloads, weak verification, and unsafe browser storage.

Why JWT mistakes matter
JWTs are often used for authentication, session management, API authorization, and access control. That means a small JWT validation mistake can become an account takeover or privilege escalation bug.
Use the JWT Security Checker to decode a token locally and look for the mistakes below.
Mistake 1: Trusting alg: none
The JWT header includes an alg value that describes the signing algorithm. Some JWT standards allow unsecured tokens for special cases, but authentication systems should not accept unsigned tokens.
If a server accepts a token with alg: none, an attacker may be able to change payload claims such as role, sub, or isAdmin and remove the signature.
Fix: reject alg: none for authentication and authorization. Configure the server to allow only the exact algorithm expected for that application.
Mistake 2: Algorithm confusion
Algorithm confusion happens when a server lets the token header influence how verification is performed. For example, a service intended to verify an RSA-signed token might be tricked into verifying a forged HMAC token using the wrong key material.
Fix: pin the expected algorithm in server configuration. Do not dynamically trust the token's alg header.
Mistake 3: Missing exp
The exp claim tells the verifier when the token expires. Without it, a token may remain usable until a separate revocation rule catches it, which many systems do not implement well.
Fix: require a numeric exp claim and reject expired tokens. For access tokens, prefer short lifetimes measured in minutes or hours.
Mistake 4: Long-lived access tokens
Long-lived tokens are convenient but risky. If an attacker steals one, they may keep access for days, weeks, or months.
Fix: keep access tokens short-lived. Use refresh tokens with rotation and stronger storage controls when persistent sessions are required.
Mistake 5: Sensitive data in the payload
JWT payloads are Base64URL encoded, not encrypted. Anyone with the token can decode the payload. This includes browser extensions, logs, proxies, crash reports, screenshots, and copied debug output.
Avoid putting these in a JWT payload:
- Passwords or password hashes
- API keys
- Refresh tokens
- Private keys or secrets
- Payment card data
- National IDs or sensitive personal data
- Unnecessary email, phone, or address fields
Fix: put only the minimum claims needed by the receiving service. Fetch private profile data from a protected API instead of embedding it in the token.
Mistake 6: Not validating iss and aud
A valid signature is not enough. A token can be validly signed by one system but intended for another system.
iss identifies the issuer. aud identifies the audience. If an API does not validate them, it may accept a token that was created for a different app or environment.
Fix: validate issuer and audience on every request that trusts a JWT.
Mistake 7: Treating decode as verify
Developers sometimes decode a JWT and trust the claims because the JSON looks correct. That is unsafe. Anyone can create a JWT-shaped string with any payload.
Fix: verify the signature before trusting claims. Decoding is only inspection.
Mistake 8: Unsafe browser storage assumptions
Storing JWTs in localStorage is simple, but JavaScript can read it. If the app has an XSS vulnerability, an attacker may steal the token.
Cookies are not automatically perfect either. A cookie-based approach needs HttpOnly, Secure, SameSite, CSRF protections where relevant, and careful domain/path scoping.
Fix: choose storage based on your threat model. Keep access tokens short-lived, reduce XSS risk, and avoid exposing refresh tokens to frontend JavaScript when possible.
Mistake 9: Ignoring key rotation
JWT systems often use a kid header to identify the signing key. If key rotation is sloppy, services may keep accepting old keys forever or fetch attacker-controlled keys.
Fix: rotate keys intentionally, keep a bounded set of trusted keys, and fetch JWKS only from trusted URLs configured server-side.
Practical review workflow
- Paste the token into the JWT Decoder.
- Confirm the header algorithm and key ID.
- Check
exp,iat,nbf,iss, andaud. - Review payload claims for sensitive data.
- Verify the signature with the expected key.
- Confirm your backend rejects modified tokens and unsigned tokens.
Quick checklist
| Check | Good result |
|---|---|
alg | Expected algorithm only |
exp | Present, numeric, short-lived |
| Payload | No secrets or unnecessary PII |
| Signature | Verified server-side |
iss and aud | Validated |
| Storage | Chosen intentionally for the app threat model |
| Key rotation | Trusted JWKS/key list, no open key fetch |
