JWT Decoder
What it does
The JWT Decoder takes a JSON Web Token and shows you what it contains — the header (algorithm and token type), the payload (claims and metadata), and the signature (treated as opaque). The header and payload are rendered as pretty JSON. Common time claims (exp, iat, nbf) are pulled out into a separate breakdown with human-readable times and a status badge — Active, Expired, or Not yet valid. Decoding happens entirely in the browser; production tokens belong nowhere near a server you do not control.
Common situations
You’re debugging an authentication failure mid-integration. The login worked, the token came back, the next API call returns 401. Decoding the token reveals the actual claims being sent — wrong audience, expired before you could use it, missing required scope, or a user_id field the receiving API doesn’t recognise. Five seconds of decoding usually surfaces the answer that twenty minutes of log reading would not.
You’re integrating with a vendor API that returns JWTs containing user metadata. The vendor’s documentation describes the expected fields; you want to confirm the actual response matches. Decoding a real token from a test call reveals what the vendor actually sends, which can differ from what their documentation describes.
You’re auditing a security incident where suspicious tokens appeared in logs. Decoding the token claims tells you who the bearer claimed to be, when it was issued, and which application it was meant for — the start of any forensic analysis. The token’s claims are what you can see; whether they are true requires the signature to be verified, which is a separate step.
You’re learning OAuth 2.0 or OpenID Connect and building intuition for what tokens contain. Decoding live tokens from real systems is the fastest way to internalise which claims appear where, what scopes look like in practice, and how access tokens differ from ID tokens.
You’re writing client-side code that reads its own token to extract a user ID or expiry. Browsers can decode their own JWTs without a verification step (the bearer trusts itself). Pasting one of your real tokens into the decoder during development is the right way to confirm the decoded shape before writing the code that depends on it.
What you need to know
A JWT is three base64url-encoded segments separated by dots: header.payload.signature. The header and payload are JSON; the signature is the cryptographic proof that the issuer signed those segments with a secret or private key. The tool splits on dots, base64url-decodes each segment, and parses the first two as JSON.
The signature is intentionally not verified. Verification requires the matching secret (for symmetric algorithms like HS256) or public key (for asymmetric like RS256, ES256). Sharing those credentials with a browser tool would be a security boundary failure. Verification is server-side work, done by the API that issued the token — the tool’s job is to show you what’s inside, not to vouch for it.
Time claims are pulled out and converted from Unix epoch seconds to readable times. The status badge compares exp and nbf to the current time, which is also how production verifiers behave (with a small clock-skew tolerance, usually ±60 seconds, which this tool does not apply).
Standard claims defined in the JWT spec:
- iss (issuer) — who created the token
- sub (subject) — who the token is about (typically the user)
- aud (audience) — who the token is for (typically an API)
- exp (expiration) — Unix timestamp after which the token is invalid
- iat (issued at) — Unix timestamp of when it was issued
- nbf (not before) — Unix timestamp before which the token is invalid
- jti (JWT ID) — unique identifier, used for revocation tracking
Custom claims can be anything. Vendors abuse this freely — application-specific user IDs, role names, scopes, feature flags, tenant identifiers, and arbitrary metadata all appear in production tokens.
JWTs are signed, not encrypted. The contents are deliberately visible — anyone with the token can read the header and payload without any key. The signature only proves the contents have not been tampered with since signing. If your application puts sensitive data (PII, secrets, credit card numbers) inside a JWT, that data is exposed to anyone who intercepts the token. JWTs should contain identification claims, not confidential data.
For tokens that genuinely need to be encrypted (rare), the JWE (JSON Web Encryption) standard exists. JWE tokens look similar to JWTs but have five segments and require a decryption key to read. This tool does not handle JWE.
Frequently asked questions
What is a JWT?
JSON Web Token — a compact, signed token format used for authentication and authorisation. Three parts (header, payload, signature) joined by dots. The payload contains claims about an identity; the signature proves the claims have not been tampered with.
Is JWT encrypted?
No. JWTs are signed, not encrypted. Anyone with the token can read its contents without any key. The signature only proves the token came from the issuer. For encrypted tokens, the JWE standard is the alternative.
How do I decode a JWT?
Split the string on dots, base64url-decode each segment. The first two are JSON (header and payload); the third is the binary signature. The tool here does this automatically, plus interprets common claims as human-readable times.
Should I trust the contents of a decoded JWT?
Decoding shows what the token claims. Verification (server-side, with the issuer’s key) confirms whether the claims are authentic. Never trust an unverified JWT in security-critical code. Use the decoded values for debugging only.
What’s the difference between an access token and an ID token?
In OpenID Connect, access tokens authorise API calls (used at API endpoints) and ID tokens identify users (used by the client app). Both are JWTs, but they have different audiences and contain different claims. ID tokens contain user profile info; access tokens typically contain less and refer to the user by ID only.
How long should a JWT be valid?
For access tokens: short, usually 5–60 minutes. The shorter, the smaller the impact of a leaked token. For refresh tokens (which mint new access tokens): longer, often hours to days. ID tokens are typically valid for the same period as the underlying session.
What does the “alg” header mean?
The signing algorithm. HS256 is HMAC with SHA-256 (symmetric — same key signs and verifies). RS256 is RSA with SHA-256 (asymmetric — private key signs, public key verifies). ES256 is ECDSA with SHA-256. Modern systems prefer asymmetric algorithms because verification can happen without the signing key.
Can I trust a JWT with alg: "none"?
Absolutely not. alg: "none" means the token has no signature. This was a real attack vector — applications that accepted unsigned tokens. Modern verification libraries reject alg: "none" by default. If you see one in the wild, the issuing system is misconfigured.
Why do my JWT segments end with .signature_placeholder?
Sample tokens used in documentation and tools often replace the actual signature with a placeholder string for clarity. The header and payload are real and decodable; the signature is not real and would fail verification.
Common problems
Problem: “A JWT has three dot-separated parts” error on what looks like a valid token.
The token is missing one or more segments, usually the signature. Some systems strip signatures when logging tokens (for security). The decoder needs at least the three-segment structure even if the signature is a placeholder.
Problem: Decoded payload shows
exp: 1700000000rather than a date.
That is a Unix timestamp in seconds. The tool’s “Validity” section converts it to readable time. If your token’s exp is in milliseconds (some non-standard implementations use milliseconds), it will appear to be very far in the future — divide by 1000 to interpret correctly.
Problem: Token decodes successfully but my API rejects it.
The decoder confirms the structure is valid. API rejection has many possible causes: signature mismatch (the API’s verification key does not match the signing key), wrong audience, expired, wrong issuer, missing scope, replay protection (jti already seen). Check the API’s specific rejection reason — most return one in the response body.
Problem: My iss and aud claims look like URLs.
That is intentional. OpenID Connect uses URLs as identifiers (the issuer’s metadata URL, the audience’s API URL). The URLs do not need to resolve; they are just unique identifiers in URL form.
Problem: The token is technically expired but the API still accepts it.
Verifiers typically allow some clock skew tolerance — a token expired by 30 seconds may still be accepted because the issuer’s clock and the verifier’s clock might differ. Don’t rely on this; treat exp as a hard deadline in client code.
Quick guides
For Node.js verification: Use jsonwebtoken‘s jwt.verify(token, secretOrPublicKey, options). Specify the algorithm explicitly in options to prevent algorithm-confusion attacks. Returns the decoded payload on success or throws on failure.
For client-side decoding: Browser’s atob() plus base64url normalisation. JSON.parse(atob(token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/'))) extracts the payload. Note: this does not verify the signature — server-side verification is still required.
For Python verification: Use pyjwt‘s jwt.decode(token, key, algorithms=['HS256']). Always pass the algorithms parameter; never accept the token’s alg claim verbatim.
Tips
- The most useful claim to check is
exp. Most “my token doesn’t work” issues are tokens that expired silently while debugging. iat(issued at) is set by the issuer’s clock. If your system clock is ahead of the issuer’s, you can briefly receive tokens withiatin the future. Some verifiers reject these; some don’t.- The
aud(audience) claim says who the token is for. APIs that accept tokens for multiple services check this — a token meant for “service-A” should fail at “service-B” even if signed correctly. - Custom claims (anything not in the standard set) appear in the payload as-is. Vendors abuse this freely — application-specific user IDs, role names, scopes, feature flags can all live here.
- The signature is not “broken” if you can read the header and payload without it. JWTs are signed, not encrypted — the contents are deliberately visible, and the signature only proves they have not been tampered with.
- Always specify the expected algorithm when verifying. The historical “alg: none” attack worked because libraries trusted the token’s own
algclaim. - For client-side code that needs to know when a token expires, decode the payload locally — verification is not needed for self-trust.
Related tools in this suite
The natural pairing is the Hash Generator — JWT signatures are HMAC variants of these hash algorithms, and a hash generator is the sanity-check tool when investigating signature mismatches. The Base64 Text tool helps when you need to decode the URL-safe base64 segments manually rather than as a complete JWT.
Take it further
JWTs that need real verification belong on a server. The services we deliver often include authentication-layer work — building or auditing the issuer, configuring the verifier, picking the right algorithm, deciding which claims to trust. A free decoder shows you what is in a token; a properly designed system decides what should be there and enforces it.