Introduction: The Token Behind Every Login
If you have built or debugged a modern web application, you have met the JSON Web Token. It is the small, cryptic string that flows between a browser and an API on nearly every authenticated request. When logins mysteriously fail, when sessions expire too soon or never expire, or when one service rejects another's token, a JWT is usually at the centre of it. This guide explains what a JWT is, how its pieces fit together, and how to debug the problems they cause — with a hands-on companion in our JWT Decoder & Inspector, which decodes and verifies tokens entirely in your browser.
Signed, Not Encrypted: The Key Mental Model
The most common misconception about JWTs is that they hide their contents. They do not. A standard JWT is signed, not encrypted. Anyone holding the token can read everything inside it. The signature does not conceal the data; it proves two things: that the contents have not been altered, and that they were issued by someone holding the signing key.
This is why a decoder can show you any token's contents without a password, and why you must never put secrets — passwords, API keys, private data — inside a JWT payload. If confidentiality is required, the token must be encrypted (the JWE standard) or sent only over secure channels. Treat the payload as public information that happens to be tamper-evident.
The Three Parts of a JWT
A JWT is three base64url-encoded segments separated by dots: header.payload.signature. Paste any token into the decoder and you will see these three parts light up.
The Header
The header is a small JSON object describing the token. Its most important field is alg, the algorithm used to sign it — HS256, RS256, ES256, and so on. It usually also carries typ ("JWT") and may include kid, a key identifier telling the verifier which key signed this particular token, which matters when a service rotates keys.
The Payload
The payload holds the claims — the actual assertions the token makes. Some are standard registered claims with defined meanings; others are custom claims your application adds, such as a user's role, tenant, or permitted scopes. Because the payload is readable, it is where you look first when debugging "this user should have access but doesn't".
The Signature
The signature is computed over the header and payload using the algorithm named in the header. It is the part that makes a token trustworthy. Change a single character of the payload and the signature no longer matches, so a verifying server will reject the token.
Standard Claims You Will See Constantly
iss(issuer) — who created and signed the token, often an identity provider's URL.sub(subject) — the principal the token represents, usually a stable user id.aud(audience) — the intended recipient. A token minted for one API should be rejected by another; mismatched audiences are a frequent cause of cross-service auth failures.exp(expiration) — a Unix timestamp after which the token must be rejected.nbf(not before) — a timestamp before which the token is not yet valid.iat(issued at) — when the token was created.jti(JWT id) — a unique id, useful for revocation lists.
The decoder converts the timestamp claims into readable dates and shows whether the token is currently valid, which turns the most common authentication bug — an expired or not-yet-valid token, often caused by server clock drift — into a one-glance diagnosis.
How Verification Actually Works
Verification recomputes the signature and compares it. With a symmetric algorithm like HS256, the same secret signs and verifies using an HMAC. Both the issuer and the verifier must share that secret, which works well within a single trust boundary but does not scale across organizations. With an asymmetric algorithm like RS256 or ES256, a private key signs and the corresponding public key verifies. This is what lets identity providers publish a public key — often at a JWKS endpoint — so any service can validate tokens without being able to forge them. In the JWT Decoder, you paste the HMAC secret or the PEM public key and the browser's Web Crypto API performs the check locally; your key never leaves the page.
Debugging the Most Common JWT Problems
"The token is rejected immediately." Check the algorithm and the signature first. A mismatch between the algorithm the issuer used and the one the verifier expects, or a wrong secret/key, will fail verification. Decode the header to confirm alg.
"It works, then suddenly stops." Look at exp. The token has expired. Decode it and read the expiry date; if it is in the past, the client needs to refresh. If tokens expire far sooner than expected, the issuer's lifetime configuration is likely too short.
"It fails only on one server." Compare aud against what that server expects, and check for clock skew affecting nbf/exp. A server whose clock is a few minutes off can reject otherwise valid tokens.
"The payload looks right but access is denied." Verify the signature. A correct-looking payload means nothing if the token is unsigned or signed with the wrong key. Decoding shows the claims; only verification proves they are trustworthy.
Security Pitfalls Every Developer Should Know
- Never trust a decoded payload. Always verify the signature server-side before acting on any claim.
- Reject "alg: none". Pin the expected algorithm; never let the token's own header dictate whether verification happens.
- Keep tokens short-lived. A long expiry turns a leaked token into a long-lived credential. Use refresh tokens for longevity, not enormous access-token lifetimes.
- Do not put secrets in the payload. It is readable by anyone who holds the token.
- Never paste live tokens into server-side online tools. That transmits your session credential to a third party. Use a client-side decoder that processes everything locally.
Access Tokens, Refresh Tokens, and Session Design
Production systems rarely use a single token. They use two, and understanding the split prevents a class of security mistakes. The access token is the short-lived JWT sent on every API request. It should expire in minutes, so that if it is intercepted it is useful only briefly. The refresh token is a longer-lived, more carefully guarded credential, usually stored in an httpOnly cookie, exchanged at the authentication server for a new access token when the current one expires.
The mistake to avoid is widening the access token's lifetime to days so users do not have to log in often. That converts a leaked access token into a long-lived credential an attacker can replay. The correct answer is a short access-token expiry plus a refresh token, which gives both convenience and safety. When you decode a token here and see an exp only a few minutes out, that is usually the system working as designed, not a bug — and reading the expiry is the fastest way to confirm your lifetimes match your security policy.
It also explains a common confusion: a user reports being "logged out" too soon, but the access token expiring is expected; the real question is whether the silent refresh is working. Decoding both tokens — the expiring access token and the longer-lived refresh token — quickly shows which half of the flow is misbehaving.
Where JWTs Fit and Where They Do Not
JWTs shine for stateless authentication across services: a verifier with the right key can validate a token without a database lookup, which scales beautifully. They are less ideal when you need instant revocation, because a signed token remains valid until it expires even if you want to cancel it sooner. Teams solve this with short lifetimes, token blocklists keyed on jti, or a hybrid of stateless verification plus a fast revocation check. Knowing this trade-off helps you decide when a JWT is the right tool and when a traditional server-side session is simpler. The decoder helps either way by making the token's claims and lifetime visible at a glance.
A Practical Debugging Workflow
- Paste the failing token into the JWT Decoder and read the algorithm and status badges.
- Check the expiry and not-before dates against the current time.
- Compare
issandaudagainst what the rejecting service expects. - If the structure looks right, paste the secret or public key and verify the signature.
- If verification fails, the issue is the key, the algorithm, or a tampered token; if it passes, the issue is a claim mismatch or server-side policy.
Conclusion
JSON Web Tokens are simple once the mental model clicks: three readable, signed segments carrying claims, trustworthy only after the signature verifies. Most authentication bugs come down to expiry, audience, algorithm, or a missing verification step — all of which you can diagnose in seconds by decoding the token and checking its claims. Keep the JWT Decoder & Inspector handy for exactly that, and pair it with our JSON Formatter when you need to read a dense payload. Everything stays in your browser, so even production tokens never leave your machine.