JWT decoder

Decode a JSON Web Token’s header and payload, expand standard claims, and flag alg:none and expired tokens.

Header
Payload
Claims explained

What this does

A JSON Web Token is three base64url-encoded segments joined by dots: header.payload.signature. This tool splits the token, decodes the header and payload to readable JSON, and explains the registered claims — iss, sub, aud, exp, nbf, iat, jti — converting the numeric timestamps to readable UTC. It then runs a few safety checks and, for HMAC tokens, can verify the signature if you supply the secret.

All of this happens in your browser. You can paste a live session token from your own application to see exactly what your auth server put in it.

Reading the signature segment

The third segment is a signature or a MAC over base64url(header) + "." + base64url(payload). The alg field in the header says how it was produced:

  • HS256 / HS384 / HS512 — HMAC with a shared secret. The same secret both signs and verifies, so it must never be shipped to a browser or a third party.
  • RS256 / PS256 / ES256 — asymmetric. The issuer signs with a private key; anyone can verify with the public key. This is what you want when the verifier and the signer are different parties.
  • none — no signature at all. Treat its presence on an auth token as a bug or an attack.

Decoding never checks the signature by itself. A JWT you pulled from a log is just as decodable whether or not it was tampered with — the decoded payload tells you what someone claims, not what is true. Only signature verification against the correct key makes it trustworthy.

Why <code>alg: none</code> and algorithm confusion matter

Two classic JWT vulnerabilities both come from trusting the token to tell you how to verify it. The first is alg: none: if your library honours it, an attacker strips the signature, edits the payload, and walks in. The second is RS256-to-HS256 confusion: the server expects an RS256 token and verifies with the RSA public key, which is not secret. An attacker changes the header to HS256 and signs the token using that public key's bytes as the HMAC secret. A naïve verifier that reads alg from the token then "verifies" the forgery. The fix for both is the same: the verifier decides the algorithm and the key out of band, and rejects any token whose header disagrees.

Worked example

Load the sample token. The header is {"alg":"HS256","typ":"JWT"} and the payload includes "iss":"auth.example.com", "aud":"api", "exp":1700003600, and "role":"admin". The claims table converts exp to a UTC time and explains that aud must equal your service's identifier or the token should be rejected. Because that sample exp is in the past, the status line reports the token as expired. Enter any secret in the verify field and the tool reports the signature does not match — the sample signature is illustrative, not real.

Limits and gotchas

  • No asymmetric verification. RS/PS/ES signatures can't be checked here without the issuer's public key; only HS256/384/512 are supported.
  • Clock skew. The expired / not-yet-valid checks use your device clock. Real verifiers usually allow a small leeway (30–60 seconds).
  • Decoding is not validation. A well-formed token can still be forged, revoked, or issued by the wrong party.
  • Encrypted tokens (JWE) have five segments and are not decodable without the key; this tool handles signed tokens (JWS) only.
  • Sensitive payloads. Anything in the payload is readable by anyone who holds the token. Don't put secrets in claims.

Frequently asked questions

Does decoding a JWT here reveal it to anyone?
No. The token is split and base64url-decoded in your browser with JavaScript. Nothing — not the token, not the payload, not a secret you enter for verification — is sent to a server. That is the point of doing it client-side: you can safely inspect a real production token.
Why is "alg": "none" dangerous?
A JWT with alg: none has no signature. The standard allows it for cases where the token's integrity is guaranteed by other means, but if an application accepts it on a normal auth path, an attacker can craft any payload they like — admin: true, someone else's user ID — and it will be trusted. Several real libraries historically accepted none by default. Your verification code must pin the expected algorithm and reject anything else, including none.
The payload looks fine but is the token actually valid?
Decoding only proves the token is well-formed. A token is valid only if the signature verifies against the issuer's key and the claims check out: exp in the future, nbf in the past, iss is who you expect, and aud contains your service. This tool checks the time-based claims and can verify an HMAC signature; it cannot verify RS/ES/PS signatures because it doesn't have the issuer's public key.
What is the difference between the "alg none" warning and an expired warning?
"alg none" is a structural red flag about how the token is signed — it should almost never appear on a real token. "Expired" means the token was legitimately issued but its exp timestamp has passed, so a correct server will now reject it. Both cause the status line to turn amber, but they are different problems: one is a possible attack, the other is normal token lifecycle.