JSON Web Tokens are the backbone of modern authentication, appearing in OAuth flows, OpenID Connect, API keys and session tokens. Yet many developers treat them as opaque strings. Understanding their structure makes debugging auth far less mysterious — and clears up a dangerous misconception about what decoding a token actually proves.
The three parts
A JWT is three Base64URL-encoded sections joined by dots: header, payload and signature. The header states the signing algorithm and token type. The payload carries the claims — the actual data about the user or session. The signature is a cryptographic seal over the first two parts, created with a secret or private key.
Common claims
- sub — the subject, usually the user ID.
- iat — issued-at, a Unix timestamp of when the token was created.
- exp — expiry, after which the token should be rejected.
- nbf — not-before, a time before which the token is invalid.
- iss and aud — the issuer and intended audience.
Decoding is not verifying
This is the single most important point about JWTs. The header and payload are only Base64-encoded, not encrypted — anyone can read them by decoding. Decoding tells you what a token claims, but it does not prove the token is authentic. Only verifying the signature, using the correct key, proves the token was issued by a trusted party and has not been tampered with. Never make a trust decision based on decoded contents alone; always verify server-side.
Debugging tips
When auth fails, decode the token and check the exp claim first — expired tokens are the most common cause. Then confirm the iss and aud match what your service expects. Because a decoder that runs in your browser never transmits the token, it is safe to inspect production tokens while debugging.