Debugging JWT Tokens
JWTs look scary because they're compact and signed, but under the hood each one is just three base64url-encoded chunks: header.payload.signature.
Decoding structure
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← header
.
eyJzdWIiOiIxMjMiLCJpYXQiOjE3MDAwMDAwMDB9 ← payload
.
<signature bytes>
Base64url-decode the first two parts to read JSON. The signature is not encrypted — it's a hash-based MAC (HS256) or an asymmetric signature (RS256). JWTs are signed, not encrypted, so never put secrets in the payload.
Standard claims
| Claim | Meaning |
|---|---|
iss |
Issuer |
sub |
Subject (usually the user ID) |
aud |
Audience |
exp |
Expiration (Unix seconds) |
nbf |
Not valid before |
iat |
Issued at |
jti |
Unique token ID |
Always check exp and verify the signature. Decoding without verifying is the #1 JWT security mistake.
Common algorithm choices
- HS256 — symmetric; shared secret. Simplest for monoliths.
- RS256 / ES256 — asymmetric; the issuer holds the private key, consumers hold only the public key. Use this when services that verify aren't the ones that sign.
- none — the "no signature" algorithm. Always reject tokens with
alg: noneon the server.
Debugging workflow
- Paste the token — confirm the header decodes cleanly.
- Check
expagainst current time (Unix epoch seconds). - Check
issandaudmatch expectations. - For HS256, paste the shared secret and verify.
- For RS256, verify against the issuer's public key (JWKS endpoint).
Our JWT Analyzer does all of this in the browser — nothing leaves your machine.