IQ
PayloadIQ
← PayloadIQ Guides

Decoding JWTs Safely

You can read a JWT in seconds without any library. What you cannot do in the browser is trust it. Here is the line between the two.

A JSON Web Token looks cryptic, but it is mostly just JSON in disguise. It has three parts separated by dots β€” header.payload.signature β€” and the first two are base64url-encoded JSON you can read instantly. Decoding one is not breaking anything: tokens are encoded for transport, not encrypted for secrecy.

What is inside

A typical token decodes to a header and a payload:

// header
{ "alg": "HS256", "typ": "JWT" }

// payload (the "claims")
{
  "sub": "1042",
  "role": "admin",
  "iat": 1717286400,
  "exp": 1717290000
}

The header names the signing algorithm. The payload carries claims: sub (the subject, usually a user id), plus standard time claims iat (issued-at), exp (expiry) and sometimes nbf (not-before), all as Unix seconds. To check whether a token is expired, compare exp against the current epoch: Date.now() / 1000 > exp.

The part that matters: the signature

The third segment is a signature computed over the header and payload using a secret (HMAC) or a private key (RSA/ECDSA). It is the only part that proves the token is genuine. Anyone can craft a token with role: "admin" in the payload β€” what they cannot do is produce a valid signature without the key.

This is why you never trust a JWT decoded in the browser. Reading the payload tells you what the token claims; only verifying the signature on the server, with the key, tells you whether those claims are real. Decode client-side to debug and display; authorize server-side after verification, every time.

Two traps to know

The alg: "none" attack: some libraries once accepted a token that declared no algorithm and skipped verification entirely. Always pin the expected algorithm on the server. The related algorithm confusion attack swaps an RS256 token for an HS256 one so the public key gets used as an HMAC secret β€” again, fixed by pinning the algorithm rather than trusting the header.

A valid signature is necessary, not sufficient

Verifying the signature proves a token wasn't forged β€” but a genuine, correctly-signed token can still be the wrong token. Once the signature checks out, the server should validate the registered claims:

  • exp β€” reject expired tokens.
  • nbf (not-before) β€” reject a token presented before it becomes valid.
  • iss (issuer) β€” confirm the token came from the identity provider you actually trust, not just some issuer. RFC 8725 recommends always validating it.
  • aud (audience) β€” confirm the token was minted for your service. If one issuer serves several apps, a token meant for app B must not be accepted by app A.

Allow a small clock-skew leeway β€” a minute or two β€” when comparing exp and nbf, so a server whose clock runs slightly fast or slow doesn't reject perfectly valid tokens. Skipping the iss and aud checks is exactly how a validly-signed token from one context gets replayed into another β€” a token-substitution attack.

When to use a decoder

Pop a token into a decoder when you're debugging an auth flow, checking why a session expired, confirming which claims your identity provider issues, or inspecting scopes. The JWT Decoder does this entirely in your browser, shows issued-at and expiry status, and never sends the token anywhere β€” which matters, because a live access token is a credential.

Common mistakes

  • Authorizing on decoded claims. Reading role: "admin" in the browser and trusting it is the classic JWT vulnerability.
  • Verifying signatures client-side. The secret would have to ship to the browser, which defeats the entire scheme.
  • Pasting live tokens into random sites. A server-side decoder receives a working credential. Use a local tool.
  • Logging full tokens. They land in log aggregators and stay valid until they expire. Log the sub, not the token.
  • Ignoring exp. A decoded token can be long expired; always check the claim before relying on it.
  • Checking only the signature. A correctly-signed token can still be expired, not-yet-valid, from the wrong issuer, or meant for a different audience β€” validate exp, nbf, iss and aud too.

Decoding a JWT is a five-second read. Trusting one is a server-side decision. Keep those two ideas separate and JWTs stop being mysterious.

Open the JWT Decoder β†’

Related guides

Browser-local JSON ToolsJSON Formatter & Validator