Decoding vs Verifying a JWT
Two different operations
A JWT is three base64url segments separated by dots: header.payload.signature. Decoding just base64-decodes the first two segments — anyone holding the token can do it, no key required. Verifying recomputes the signature over header.payload with a secret or public key and compares it.
That distinction is the single most common source of auth bugs: a decoded token tells you what it claims, never that the claim is true.
Worked example
Take a token whose payload decodes to:
{"sub":"user_8412","role":"admin","iat":1756600000,"exp":1756603600}- Decoding tells you the token says the user is an admin.
- Verifying tells you the issuer actually signed that statement and it has not been edited.
- Anyone can craft the first version in a text editor; only the key holder can produce the second.
Claims worth checking every time
After signature verification, validate the registered claims — a valid signature on an expired or foreign token is still a rejection:
exp— expiry; reject if past, allowing a few seconds of clock skew.nbf/iat— not-before and issued-at; guards replay of pre-minted tokens.iss— must equal your expected issuer URL exactly.aud— must contain your own API identifier, or a token minted for another service will pass.sub— the user identifier you attach the session to.jti— unique ID, needed if you maintain a revocation list.
When a decoder is the right tool
Use a decoder for debugging: checking why an API returned 401, confirming which scopes a provider issued, reading an exp that looks wrong, or comparing the claims two environments produce. It is the fastest way to see whether the problem is the token or your validation code.
Never use decoding as an authorisation step in server code. Libraries make the mistake easy — jwt.decode() skips the signature, jwt.verify() does not.
Algorithm pitfalls
Two classic vulnerabilities come from trusting the token's own header. alg: none asks the verifier to accept an unsigned token. Algorithm confusion swaps RS256 for HS256 so the public key gets used as an HMAC secret. In both cases the fix is the same: pin the accepted algorithm list in your verification call instead of reading it from the header.
Prefer asymmetric signing (RS256/ES256) whenever a token is verified by more than one service, so only the issuer holds a signing key.
Storage and lifetime
Keep access tokens short-lived — minutes, not days — and pair them with a refresh token stored in an httpOnly cookie. JWTs cannot be revoked once issued, so a long expiry means a stolen token stays valid until it lapses. If you need instant revocation, keep a server-side deny list keyed by jti.
Decode a token safely
Paste a token into the JWT Decoder — header, payload and expiry are parsed entirely in your browser, so production tokens are never transmitted. Pair it with the Base64 Decoder when you need to inspect a single segment by hand.
Try the JWT Decoder →Decode a JWT online to read its header, payload claims and expiry status. Free JWT decoder that runs 100% in your browser — tokens are never uploaded.Frequently asked questions
Is a JWT encrypted?
No. A standard JWS token is only signed, so its payload is readable by anyone. Use JWE, or simply keep secrets out of the payload.
Can I verify a JWT in the browser?
You can verify a signature with a public key, but the check is meaningless for authorisation — the client can skip it. Verify on the server.
Why does my token fail verification after a deploy?
Usually a rotated signing key or a different issuer per environment. Decode the token and compare its iss and kid values.