A decoded JWT payload looks trustworthy — clean JSON, a user ID, some roles, an expiry date. It's easy to forget that "decoded" and "verified" are completely different operations, and treating a decoded-but-unverified token as truthful is a real, exploitable mistake.
Anyone can produce a JWT that decodes perfectly
A JWT's header and payload are just base64url-encoded JSON — see our JWT anatomy post for the full breakdown. Base64url is not encryption and requires no secret to produce. That means anyone — including someone with malicious intent — can hand-craft a JWT with whatever header and payload they want:
{
"sub": "1234567890",
"name": "Alice",
"role": "admin"
}Encoding that into a valid-looking JWT shape takes seconds and no special tools. If a system only checks "does this decode into a well-formed token" without checking the signature, that "role": "admin" claim is trusted on faith — which is exactly the vulnerability class this mistake creates.
The signature is the only part that proves anything
The signature segment is computed from the header and payload using a secret (or private) key that only the legitimate issuer holds. Verifying it means recomputing that signature with the correct key and confirming it matches. That's the entire trust mechanism — the payload is public and readable by anyone, but only someone holding the signing key can produce a signature that verifies successfully for a given payload. No verification step, no trust guarantee, regardless of how legitimate the payload looks.
Why our JWT Decoder deliberately never claims to verify
Verifying a signature requires the secret or public key the token was signed with — that's not something that should ever be typed into a random web tool, client-side or not, since a signing secret is exactly the kind of credential that must stay on your own server. Our JWT Decoder reads the header and payload (no key needed for that) and displays the signature segment as-is, but never attempts to check it — and never will. A tool that silently skipped verification while implying it happened would be far more dangerous than one that's upfront about only decoding.
What "verify, don't just decode" looks like in practice
- Client-side, browser-only tools (including this one) are for inspection and debugging — reading claims, checking expiry, confirming a token has the shape you expect. Never the basis for an authorization decision.
- Server-side verification, using a proper JWT library and the actual signing key or public key, is the only place a token's claims should ever be trusted for something that matters — granting access, authorizing an action, trusting an identity claim.
If your own code reads a JWT's payload and acts on it without first calling your JWT library's verify function, that's the exact gap being described here — decoding succeeded, but nothing was actually proven.