Common JWT Mistakes (and How to Avoid Them)
JWT is easy to misuse. Here are the common mistakes and how to avoid them.
Common JWT Mistakes
JWT is easy to misuse. Here are the common mistakes and how to avoid them.
Mistake 1: Storing JWT in LocalStorage
LocalStorage is open to XSS. Any script (including injected ones) can read it. Fix: store JWT in an httpOnly, secure, sameSite cookie. Scripts cannot read it.
Mistake 2: No Expiry
You sign a token with no expiry. It lives forever. If stolen, the attacker has access forever. Fix: always set expiresIn. Use short expiry (15 min) with a refresh token (7 days).
Mistake 3: Putting Sensitive Data in the Payload
The payload is base64-encoded, not encrypted. Anyone with the token can read it. Fix: only put non-sensitive claims (userId, role). Never put passwords, secrets, or PII in the payload.
Mistake 4: Using jwt.decode Instead of jwt.verify
jwt.decode just reads the payload without checking the signature. An attacker can forge a token. Fix: always use jwt.verify. It checks the signature and expiry.
Mistake 5: Weak JWT Secret
You use 'secret' or 'password' as the JWT secret. An attacker can forge tokens. Fix: use a long random string (at least 32 chars). Store it in an env var. Rotate periodically.
Mistake 6: Not Handling Expired Tokens
The client gets a 401 on every request after the token expires. Fix: use a refresh token to get a new access token silently. Or handle 401 in the client and redirect to login.
Mistake 7: Not Revoking on Logout
You clear the cookie but the token is still valid until it expires. Fix: use short expiry so the window is small. For real revocation, use a refresh token (stored in DB) and delete it on logout.
Mistake 8: Same Secret Across Environments
You use the same JWT secret in dev and prod. A leak in dev exposes prod. Fix: use different secrets for dev and prod. Load from env vars.
Mistake 9: No Algorithm Verification
You accept any algorithm in the header. Some libraries have had vulnerabilities where 'none' alg was accepted. Fix: specify the algorithm in jwt.verify: { algorithms: ['HS256'] }.
Mistake 10: Big Payloads
You put everything (user profile, permissions, history) in the payload. Tokens get huge. Every request sends it. Fix: only put the userId. Load the rest from the DB on each request.
The Takeaway
Common JWT mistakes: storing in LocalStorage (use httpOnly cookies), no expiry (use short expiry with refresh), sensitive data in payload (only put userId and role), jwt.decode instead of verify, weak secret, no expired token handling, no revocation on logout, same secret across envs, no algorithm verification, and big payloads.
Storing in LocalStorage, no expiry, sensitive data in payload, jwt.decode instead of verify, weak secret, no expired token handling, no revocation on logout, same secret across envs, no algorithm verification, and big payloads.
LocalStorage is open to XSS. Any script (including injected ones) can read it and send the token to an attacker. Use an httpOnly, secure, sameSite cookie. Scripts cannot read it.
jwt.decode just reads the payload without checking the signature. An attacker can forge a token with any payload. jwt.verify checks the signature and expiry, so forged tokens are rejected.
Only non-sensitive claims: userId and role. The payload is base64-encoded, not encrypted. Anyone with the token can read it. Never put passwords, secrets, or PII in the payload.
Use short expiry so the window is small. For real revocation, use a refresh token (stored in DB) and delete it on logout. The access token expires in 15 min anyway. Without refresh tokens, you cannot truly revoke a JWT before expiry without a blacklist.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

