A JSON Web Token is just base64-encoded JSON with a cryptographic signature attached. Decoding a JWT means reading that JSON payload — anyone can do this with no secret key at all, since it’s not encrypted, just encoded. Verifying a JWT means checking that signature against your secret key to confirm the token wasn’t tampered with.
Why this distinction matters
Most JWT libraries expose both operations separately — for example, jwt.decode() versus jwt.verify() in the Node.js jsonwebtoken package. They have similar names and similar return values (a JSON object), which makes it easy for an AI tool — or a developer moving fast — to call decode() when the code actually needs verify().
Wondering if your code has any of this? Scan a public repo free, no account needed.
Scan a repo →What goes wrong
If your authentication middleware decodes a token and trusts the payload without verifying the signature, anyone can construct their own JWT with {"role": "admin", "userId": "anyone"}, send it to your API, and be treated as an authenticated admin. No secret key, no brute force, no exploit required — just a token built by hand.
How to check your code
- Search your codebase for every call to a JWT decode function
- Confirm each one is followed by, or replaced with, a signature verification step
- Check that the verification step actually checks the result — not just that it doesn’t throw
- Confirm the signing secret isn’t a fallback default value
Why this is common in AI-generated auth code
Decode-only JWT handling often appears in early scaffolding — a developer (or an AI tool) wires up the happy path first, gets login working, and the verification step either never gets added or gets added to one route but not all of them. It’s a small omission with a complete authentication bypass as the consequence.