Reading a JWT Without a Library (And Why You Shouldn't Trust It)
A JWT is three Base64 chunks joined by dots. Anyone can read one — which is exactly why decoding is not verifying.
A JSON Web Token looks opaque. It isn't. It's three Base64url strings joined by dots, and two of them are plain readable JSON.
The anatomy
eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiIxMjMifQ . SflKxwRJSMeKKF2QT4f...
header payload signature
Header — the signing algorithm, e.g. {"alg":"HS256","typ":"JWT"}.
Payload — the claims: who the token is about, who issued it, when it expires.
Signature — proof that a party holding the secret produced it.
Split on the dots, Base64url-decode the first two, and you have the contents. No key required.
The claims worth knowing
| Claim | Meaning |
|---|---|
iss | Issuer — who created it |
sub | Subject — who it's about |
aud | Audience — who it's for |
exp | Expiry, as a Unix timestamp |
nbf | Not valid before |
iat | Issued at |
jti | Unique token ID |
exp and iat are seconds, not milliseconds. Multiply by 1000 before handing them to a JavaScript Date, or you'll conclude every token expired in 1970.
Decoding is not verifying
This is the part that matters. Reading a JWT tells you what it claims. It tells you nothing about whether those claims are true. Only checking the signature against the issuer's key does that.
The classic vulnerability is a server that decodes a token, reads {"role":"admin"} and believes it. An attacker edits the payload, re-encodes it, and walks in. Related: the alg: none attack, where a token declares it isn't signed at all and a naive library accepts it.
Rule: never make an authorisation decision from a decoded-but-unverified token.
What decoding is genuinely useful for
- Debugging "why am I getting a 401" — usually
expin the past or the wrongaud - Confirming which environment issued a token by reading
iss - Checking that a claim your app depends on is actually present
- Reading the expiry to size your refresh window
SwitchPDF JWT Decoder shows the header, the payload, and whether the token has expired, entirely in your browser. That last part matters: a JWT is a live credential, and pasting one into a server-side decoder means handing your session to a stranger.
Anyone holding the token can read it
Because the payload is only encoded, never put anything private in it. No email addresses, no internal IDs you'd rather not leak, no permissions detail you consider sensitive. Assume the user, their browser extensions and anyone with access to their machine can read every claim.
Bottom line
Decode freely for debugging — it's just Base64. Verify before you trust. And treat the payload as public information, because to anyone holding the token, it is.
Related articles
Reading a Website's Security Headers (And What to Fix First)
Six HTTP headers do most of the browser-side security work. Here is what each one stops, and the order worth fixing them in.
PDF Security Best Practices for Legal and Compliance Teams
A practical security checklist for handling sensitive PDFs in a small legal or compliance team.
Understanding (and Stripping) PDF Metadata for Privacy
Every PDF carries metadata: author name, software used, creation date, sometimes location data. Here's how to see and remove it.