🔑 JWT Token Decoder & Inspector
A JWT decoder that also verifies the signature in your browser: HS256 with a secret, RS256 and ES256 with a public key. Claims, expiry and alg:none flagged.
Verification runs in your browser through the Web Crypto API and nothing is transmitted. Even so, a production signing secret pasted into any web page has been typed into a browser and may sit in your clipboard and form history — use a test key.
{
"alg": "HS256",
"typ": "JWT"
}HS256: HMAC using SHA-256.
{
"sub": "1234567890",
"name": "Alex Turning",
"iat": 1516239022,
"exp": 1799999999,
"role": "admin"
}sub— Subject — who the token is aboutiat— Issued At — when it was createdexp— Expiration Time — must not be accepted at or after this
What JWT Token Decoder & Inspector Does
A JSON Web Token is three Base64url-encoded segments separated by dots: a header describing the signing algorithm, a payload of claims, and a signature over the first two. Decoding the first two segments requires no key at all — they are encoded, not encrypted.
That property is the source of the most common and most costly JWT misunderstanding. Anyone holding a token can read every claim in it. Putting an email address, a role, an internal user ID or anything else sensitive in a JWT payload is equivalent to printing it on the outside of the envelope.
This decoder runs entirely in your browser and does not transmit the token. It shows the header, the decoded claims and the expiry status. It deliberately does not verify the signature, because verification requires the secret or public key — and pasting a signing secret into any web page is a mistake.
How to Use JWT Token Decoder & Inspector
- Paste your encoded JSON Web Token (JWT) into the text area
- Instantly inspect the decoded Header and Payload JSON structures
- Check token expiration status and timestamp validity
- Optionally paste the shared secret (HS256) or PEM public key (RS256, ES256) to verify the signature in your browser
JWT Structure
Example: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.dBjftJeZ4CVP… — three dot-separated segments.
| Segment | Contents | Encrypted? |
|---|---|---|
| Header | Algorithm (alg) and token type (typ) | No — Base64url only |
| Payload | Claims: subject, expiry, issuer, custom fields | No — Base64url only, readable by anyone |
| Signature | HMAC or digital signature over header.payload | Not encrypted; proves integrity, not confidentiality |
Registered Claims (RFC 7519 §4.1)
All are optional, but each has a defined meaning that libraries rely on.
| Claim | Name | Meaning |
|---|---|---|
| iss | Issuer | Who created and signed the token |
| sub | Subject | Who the token is about — usually a user ID |
| aud | Audience | Who the token is intended for; reject if it is not you |
| exp | Expiration Time | Unix seconds after which the token must be rejected |
| nbf | Not Before | Unix seconds before which the token must be rejected |
| iat | Issued At | Unix seconds when the token was created |
| jti | JWT ID | Unique identifier, used to prevent replay |
Source: RFC 7519 — JSON Web Token
Common Signing Algorithms
| alg | Type | Key | Notes |
|---|---|---|---|
| HS256 | HMAC + SHA-256 | Shared secret | Verifier can also forge tokens — same key both ways |
| RS256 | RSA + SHA-256 | Private/public pair | Verifiers only need the public key; standard for OIDC |
| ES256 | ECDSA P-256 + SHA-256 | Private/public pair | Much shorter signatures than RS256 |
| none | Unsecured | None | Must be rejected. Accepting it means accepting forged tokens |
Every JWS Algorithm and What the Spec Requires of It
RFC 7518 §3.1, in full. The requirement column is the specification's own — only HS256 is Required of a conforming implementation, which is why it is the one you meet everywhere. "Recommended+" means ES256 is expected to become Required in a future revision.
| alg | Algorithm | Implementation requirement |
|---|---|---|
| HS256 | HMAC using SHA-256 | Required |
| HS384 | HMAC using SHA-384 | Optional |
| HS512 | HMAC using SHA-512 | Optional |
| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 | Recommended |
| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 | Optional |
| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 | Optional |
| ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
| ES384 | ECDSA using P-384 and SHA-384 | Optional |
| ES512 | ECDSA using P-521 and SHA-512 | Optional |
| PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 | Optional |
| PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 | Optional |
| PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 | Optional |
| none | No digital signature or MAC performed | Optional |
Decoding and Verifying in Your Language
The libraries differ; the rule does not. Pass the algorithms you expect as a fixed list. A verifier that takes the algorithm from the token's own header is letting the attacker choose how their forgery gets checked.
| Language | Usual library | The thing to get right |
|---|---|---|
| JavaScript / Node | jsonwebtoken, or jose for JWK and JWKS | Pass an explicit algorithms list; do not rely on the default |
| Python | PyJWT | algorithms= is documented as "do not compute from the alg in the token itself" — hard-code it or configure it beside the key |
| Java | java-jwt (Auth0) or jjwt | Build the verifier around one algorithm object rather than reading the header |
| Go | golang-jwt/jwt | Check the method inside the keyfunc — the classic mistake is returning a key without looking at it |
| PHP | firebase/php-jwt | Pass a Key with its algorithm, not a bare string |
| .NET | System.IdentityModel.Tokens.Jwt | Set ValidAlgorithms in the validation parameters |
| Browser | Web Crypto API | What this page uses — importKey then verify, no dependency |
| Shell | base64 -d and jq for reading | Reading is trivial; verifying in shell is not — use a library |
How to Read Your Result
What a tampered token actually looks like
This page shipped one for months without anyone noticing, which is the point. Its payload read {"sub":"1234567890","name":"Alex Turning","iat":1516239022,"exp":1799999999,"role":"admin"} and its signature was SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c — the famous jwt.io signature, which is computed over a completely different payload, {"sub":"1234567890","name":"John Doe","iat":1516239022}. Somebody had edited the claims, granted themselves an admin role and an expiry three years out, and kept the old signature. It decodes perfectly. Every field displays. Nothing about reading it reveals the problem: only recomputing the HMAC over header.payload and comparing does, which is why "it decoded fine" is never evidence that a token is genuine.
Verifying HS256 needs the secret; verifying RS256 does not
This is the practical difference between the families and it decides which one you should be using. HMAC signs and verifies with the same shared secret, so anyone who can check a token can also mint one — fine inside a single service, dangerous the moment a second party needs to validate. The RSA and ECDSA families split it: the issuer holds the private key, everyone else verifies with a public key that grants no signing power. That is why OpenID Connect providers publish a JWKS endpoint of public keys and sign with RS256 or ES256. RFC 7518 also sets a floor on the HMAC side: "a key of the same size as the hash output (for instance, 256 bits for HS256) or larger MUST be used" — a short passphrase does not meet it, however strong it looks.
The "alg: none" attack
The specification permits an unsecured JWT with alg set to "none" and an empty signature. Libraries that honored the header's own claim about which algorithm to use would accept an attacker-modified token with the signature stripped. Any verifier must decide the expected algorithm itself and refuse anything else — never trust the token to tell you how to check the token.
Algorithm confusion (RS256 → HS256)
A related flaw: an attacker changes alg from RS256 to HS256 and signs with the server's public key as if it were an HMAC secret. If the library selects the verification method from the header, it validates successfully. The public key is, by definition, public. Pin the algorithm server-side.
Expiry and revocation
exp is a Unix timestamp in seconds, not milliseconds — a common bug that produces tokens valid until the year 56000. More fundamentally, a signed JWT cannot be revoked: it is valid until it expires, because verification is offline. Short lifetimes plus a refresh token, or a server-side deny list, are the usual mitigations.
Limitations & Accuracy Notes
- Verification here proves the signature matches the key you supplied. It says nothing about whether that is the right key, whether the issuer is one you trust, or whether the token has been revoked — all of which your server still has to decide.
- Signature checking covers the HS, RS, PS and ES families through the Web Crypto API. EdDSA (Ed25519) is not covered because browser support for it is still uneven, and JWE encrypted tokens are a different format entirely.
- Never paste a production token containing live session credentials into any online decoder, including this one. Although decoding here happens locally in your browser, the safe habit is to use tokens from a development environment.
- JWE (encrypted JWTs) have five segments rather than three and cannot be read without the decryption key. This decoder handles JWS, the signed three-segment form.
- Claim names beyond the registered set are application-defined. A "role" or "scope" claim means whatever the issuing system decided it means.
Frequently Asked Questions
What are the three parts of a JWT token?
Is my secret key exposed when decoding a JWT here?
Can decoding a JWT verify that it is genuine?
Why shouldn’t I put sensitive data in a JWT payload?
Can this verify the JWT signature, not just decode it?
Why does my token show "invalid signature"?
How do I decode a JWT with the secret to check it is genuine?
What key size does HS256 need?
Is my token sent anywhere?
Does decoding a JWT verify it?
Is the payload encrypted?
What do exp, iat and nbf mean?
What is the "alg: none" vulnerability?
Can I edit a token here and have it still work?
References & Further Reading
- RFC 7519 — JSON Web Token (JWT) — The core specification and registered claim definitions
- RFC 8725 — JSON Web Token Best Current Practices — Documents the alg:none and algorithm-confusion attacks and their mitigations
- RFC 4648 — Base64url encoding — Section 5 defines the URL-safe alphabet JWT segments use
- RFC 7518 — JSON Web Algorithms (JWA) — Section 3.1 is the full alg table with its Required/Recommended/Optional column; 3.2 sets the HMAC minimum key size; 3.6 says implementations "MUST NOT accept Unsecured JWSs by default"
- PyJWT — API reference — Source for the warning not to compute the algorithms parameter from the token's own alg header