Blog

Debugging JWTs without sending them to a server

JWTSecurityAuth

A JWT is not encrypted. The header and payload are just base64url-encoded JSON — anyone who has the token can read every claim inside it without knowing the signing secret. That's fine for its intended use (an app reading its own token), but it means a JWT should be treated exactly like a password: it should never be pasted into a tool you don't trust, because "decoding" a JWT is functionally identical to reading it in plaintext.

This is the whole reason our JWT Decoder runs entirely in the browser — the token never leaves your machine, there's no network request to inspect it, and it works the same with your laptop offline. When you're debugging an auth issue, that matters: you're often pasting a real, currently-valid production token.

What to check first

  • `exp` (expiry) — the single most common cause of "random" 401s. Compare it against the current Unix time, not against when the token was issued.
  • `iat` and `nbf` — a token issued in the future (clock skew between services) will be rejected by strict verifiers.
  • `iss` and `aud` — a token minted for one service but presented to another will fail audience checks, and the error message rarely says so explicitly.
  • The algorithm in the header (`alg`). If it says `none`, or if it doesn't match what your verifier expects (`HS256` vs `RS256`), that's a configuration bug, not a token bug.

What decoding won't tell you

A decoder shows you the claims, but it can't tell you whether the signature is valid — that requires the verifying secret or public key, which the decoder deliberately never asks for. If the payload looks correct but requests still fail with an auth error, the signature (or the verifier's key configuration) is almost always the actual problem, not the claims.

Try it on the JSON Formatter's sibling tool: paste a token into the JWT Decoder and check `exp` first — it catches the majority of real-world "my login broke" reports.