</>DevTools

JWTJWT Decoder

Decode and inspect JWT tokens

How to Read a JWT — and Why Decoding Is Not Verifying

A JWT is three dot-separated parts: header, payload, and signature. The first two are merely base64url-encoded, not encrypted, so anyone can read them — which is what this tool does. One distinction matters more than any other here: decoding is not verification.

The three parts

The token is header.payload.signature. The header carries the signing algorithm (alg), the token type (typ), and optionally a key identifier (kid) used to select a key. The payload is a set of key-value pairs called claims. The signature is computed over the first two parts joined together, using a shared secret or a private key.

How a token is built
header  = {"alg":"HS256","typ":"JWT"}
payload = {"sub":"1234","name":"Jane Doe","exp":1735689600}

signingInput = base64url(header) + "." + base64url(payload)
signature    = HMACSHA256(signingInput, secret)

token = signingInput + "." + base64url(signature)

What the standard claims mean

The registered claims from RFC 7519 have deliberately terse names, to keep tokens small.

ClaimNameMeaning
ississuerWho issued the token
subsubjectWho the token is about — usually a user ID
audaudienceWho should accept it. Always check this during verification
expexpiration timeExpiry as Unix seconds; reject after this instant
nbfnot beforeNot valid before this instant
iatissued atWhen it was issued
jtiJWT IDUnique token identifier — used for replay prevention or blacklists

How to use it

  1. Paste the whole JWT. Drop any Bearer prefix and start from eyJ...
  2. The header and payload are shown as JSON. Check the alg value and the claims.
  3. If exp is present, compare it against the current time to see whether the token has expired.
  4. The signature is displayed but not validated — validation requires the key.

Decoding versus verifying

Decoding reverses base64url so you can read the text; it needs no key at all. Verifying is the cryptographic check that the signature really was produced from this payload with that key. A server must verify — if it decodes and then trusts the payload, an attacker can put whatever they like in it.

The vulnerable pattern you'll actually encounter is jwt.decode(). In most libraries decode does not check the signature and verify does. If your auth middleware calls decode, it isn't authenticating anything; it's just parsing.

Dangerous versus correct
// dangerous: no signature check
const payload = jwt.decode(token);
if (payload.role === "admin") { /* anyone gets in */ }

// correct: verifies signature, expiry, issuer, and audience
const payload = jwt.verify(token, secret, {
  algorithms: ["HS256"],     // pin the algorithm
  issuer: "https://auth.example.com",
  audience: "my-api",
});

Algorithm confusion attacks

The two best-known JWT vulnerabilities both come from trusting the header's alg value. The first sets alg to none and sends an empty signature; if the verifier is written to accept none, an unsigned token passes.

The second swaps RS256 for HS256. RS256 signs with a private key and verifies with a public one, so if an attacker changes alg to HS256, the verifier uses the public key as an HMAC secret. The public key is, by definition, public — so the attacker can forge a valid signature.

Both have the same defense: pin the accepted algorithms in your code and never take the token header's word for it.

What not to put in the payload

  • Passwords, card numbers, national ID numbers — anything readable is readable by everyone
  • Rapidly changing permissions — a token is hard to revoke before it expires
  • Large data — JWTs ride in a header on every request, so size is bandwidth cost
  • Identifiers that reveal internal system structure — that's information disclosure

Frequently Asked Questions

Is it safe to paste a production token here?
Decoding happens entirely in your browser with no network transmission. That said, a valid access token *is* a credential, so pasting an unexpired production token into any third-party site is generally inadvisable. Prefer a development-environment or already-expired token.
Are JWTs encrypted?
A conventional JWS-format JWT is signed, not encrypted — tampering is detectable, but reading is not prevented. If contents must be hidden you need JWE (JSON Web Encryption), though in practice teams solve this by simply not putting sensitive data in the payload.
The token is past exp but the server still accepts it.
Likely the verification options disable expiry checking (ignoreExpiration), the server clock is badly skewed, or the code only decodes without verifying. Note also that exp is Unix *seconds* in UTC — writing milliseconds there, a value 1000× too large, is a common mistake.
Can I revoke a token immediately?
Not directly: JWTs are designed so the server holds no state about them. The practical approach is short access-token lifetimes (minutes) refreshed via a refresh token, plus — when instant revocation is genuinely required — a jti blacklist or a per-user token version stored server-side. Adopting the latter gives up some of the statelessness benefit.
Where should I store the token?
localStorage is readable by any script that runs, so XSS drains it. A cookie with HttpOnly, Secure, and SameSite is safer against XSS but needs CSRF protection. The common recommendation is a refresh token in an HttpOnly cookie with the access token held only in memory.
What is kid for?
It's a key identifier, used when an issuer runs multiple keys or rotates them, so the verifier knows which public key to use. In OIDC setups you fetch a key list from a JWKS endpoint and match on kid. Beware that implementations which feed kid straight into a file path or SQL query create path-traversal and injection holes — always validate it.

💡 Note: If the token splits into five parts rather than three, it's a JWE (encrypted JWT), not a JWS — you cannot read its payload without the key.

🔗Related Tools🔐 Crypto / Security