Authentication — proving who someone is — is a different problem from authorization, which is deciding what they're allowed to do once you know. Broken access control, the authorization failure, gets a lot of attention because it's common and usually a simple missing check. Authentication has its own recurring mistake patterns, and because so many of today's applications hang their entire session model on a JSON Web Token, those mistakes tend to look the same across otherwise unrelated codebases.
The "alg: none" and algorithm-confusion mistakes
A JWT carries its signing algorithm in its own header, and some JWT libraries — especially older ones — will trust whatever algorithm the token claims rather than the one the server actually expects. Historically this allowed an attacker to submit a token with "alg": "none" and have it accepted with no signature at all. A closely related mistake is algorithm confusion: a server configured to verify tokens with an asymmetric algorithm like RS256, using a public key, that can also be tricked into accepting a token signed with HS256 using that same public key as if it were a shared HMAC secret. Both are library-configuration issues, and both are worth confirming explicitly rather than assuming the library's defaults are safe.
Signature verification that isn't actually happening
Most JWT libraries expose a decode function and a separate verifyfunction — decode reads the payload without checking the signature at all, verify does both. It is an easy mistake to call decode because it's convenient for reading a claim, and never realize the signature was never checked. Any code path that trusts a claim from a decoded-but-unverified token is equivalent to trusting whatever the client sends, no matter how legitimate the rest of the authentication flow looks.
Token lifecycle mistakes
- No expiration claim set, or an expiration far longer than is reasonable for what the token grants access to.
- No way to revoke a token before it naturally expires — a purely stateless JWT setup has no answer for "log this user out everywhere" or "invalidate every token issued before this password change."
- Refresh tokens that can be reused indefinitely rather than rotated on each use, with reuse of an old refresh token treated as a sign of theft.
- Sensitive claims (role, permission level) embedded in the token and trusted long after they might have changed server-side.
Session-cookie mistakes for teams not using JWTs
Traditional server-side sessions have their own well-known failure modes worth checking in review: session fixation, where a session identifier issued before login is kept after login instead of being rotated, letting an attacker who set that ID in advance hijack the authenticated session; missing Secure, HttpOnly, or SameSite cookie flags on the session cookie; and session identifiers generated with anything less than a cryptographically secure random source, making them guessable.
Checklist for reviewing authentication code
- The JWT library is configured to accept only the specific algorithm the server expects, not whatever the incoming token claims.
- Every code path that reads a claim from a token has gone through actual signature verification, not just a decode call.
- Tokens have a reasonable expiration, and there is a real mechanism to invalidate them early when needed.
- Refresh tokens rotate on use, with reuse of a stale token treated as a signal, not ignored.
- Session identifiers are rotated after login and carry
Secure,HttpOnly, and an explicitSameSitesetting.
Read about reviewing broken access control before it ships →