JSON Web Tokens are everywhere: authentication, single sign-on, API authorization. They are also one of the most misunderstood pieces of the modern web — mostly because people confuse decoding a token with verifying it.
Here is what is actually inside a JWT, and the one mistake that quietly turns a "secure" auth system into an open door.
The three parts
A JWT is just a string with three dot-separated segments:
header.payload.signature
- Header — the token type and the signing algorithm (e.g. HS256, RS256).
-
Payload — the claims: user ID, roles, expiry (
exp), issuer (iss), audience (aud). - Signature — what lets a trusted party confirm the token was not tampered with.
The first two segments are Base64URL-encoded JSON. That is all. They are not encrypted, and anyone holding the token can read them.
Decoding is not verification
This is the part that bites people.
Decoding a JWT reveals its contents — nothing more. It does not prove the token was issued by your server, or that it has not been modified.
To actually trust a token you must:
- Verify the signature with the correct key or secret.
- Check
exp— the token has not expired. - Check
issandaudmatch what you expect.
Skip any of those and you have effectively built an API where anyone can mint themselves an admin token.
Decoding a payload by hand
Sometimes you just want to see what is inside. The payload is Base64URL, so:
JavaScript
const [, payload] = token.split('.')
const json = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')))
console.log(json)
Python
import base64, json
payload = token.split('.')[1]
padding = '=' * (-len(payload) % 4)
print(json.loads(base64.urlsafe_b64decode(payload + padding)))
PHP
[$header, $payload, $sig] = explode('.', $token);
echo base64_decode(strtr($payload, '-_', '+/'));
Note that none of these snippets verify anything — they only decode.
Common pitfalls
- Putting secrets in the payload. Anyone can read it.
- Trusting claims without verifying the signature.
-
Ignoring
expandaud. An expired token that never expires is a gift to an attacker. -
Accepting the algorithm from the token itself. Always pin the expected
algon the server side.
FAQ
Can anyone read a JWT? Yes. The header and payload are encoded, not encrypted.
Does decoding prove the token is valid? No. Validity comes from signature and claim verification.
Is my token uploaded when I inspect it? It should not be. Some online decoders send the token to a server, which is a bad idea for a credential — decoding can happen entirely in the browser.
If you need to eyeball a token quickly, I built a free JWT Decoder that runs entirely client-side, so nothing leaves your browser. (It is my own project — the explanation above stands on its own.)
Top comments (0)