Splits the token at the periods, base64url-decodes each part, parses the header and payload as JSON. Surfaces the signature as raw bytes. The 'JWT' format is technically a JWS (JSON Web Signature) in compact serialization — this is what 99% of real-world JWTs are.
Free JWT decoder — decode JWT tokens, inspect claims, verify signatures, check expiration. Paste any token to see its full structure.
JWT Decoder splits a JSON Web Token into its three base64url-encoded parts (header, payload, signature), decodes the JSON, surfaces token status (validity window, expiration countdown, time since issued), and optionally verifies the signature against your secret or public key. Supports all standard JWS algorithms — HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512, and EdDSA. Auto-updates as you paste or edit. All decoding and signature verification happens in your browser; tokens never leave your device.
What gets decoded and how
Six capabilities. The first three run automatically on any pasted token; the fourth (signature verification) is optional and key-dependent. Privacy is the differentiator vs. other online JWT decoders — most send your token to their servers, which is exactly the wrong shape for an auth-debugging tool.
Live countdown to expiration. Time since issued. Pre-validity period check (nbf). Each standard time claim is converted from Unix timestamp to your local timezone. Catches the most common JWT debugging question — 'is this token expired?' — without manual conversion.
Paste your HMAC secret (for HS*) or public key (for RS*, ES*, PS*, EdDSA) to verify the signature. Tells you whether the token was actually signed by the expected key. The most underused JWT feature — far too many apps decode tokens without verifying.
Every algorithm RFC 7518 defines. HMAC variants for shared-secret use, RSA variants for traditional public-key, ECDSA and EdDSA for modern elliptic-curve. Includes detection of the dangerous `alg: none` (which the tool decodes but flags as a security risk).
Header, payload, signature, status, and verification result all refresh as you paste or type. Useful for diffing two tokens side-by-side, or for watching what changes when you regenerate a token from your auth server.
JWTs frequently contain user IDs, roles, internal IDs, and other auth-relevant data. Pasting them into a server-based decoder is a real data-exfiltration risk. This tool runs entirely client-side using the Web Crypto API — your tokens, secrets, and keys never touch our servers.
Why a JWT decoder is the most-reached-for tool in any auth toolkit
JWTs are everywhere in modern auth — OAuth 2.0 access tokens, OpenID Connect ID tokens, API keys, signed URLs, microservice authentication, magic links, email-confirmation tokens. Five recurring needs that drive developers to a JWT decoder multiple times a week:
- Debugging auth failures Your API call returns 401. Is the token expired? Does it have the right audience claim? Is the scope correct? Decoding the token is the fastest path from 'something's wrong' to 'here's exactly what's wrong'. Three seconds with a decoder beats ten minutes in a debugger.
- Auditing logs and config files JWTs end up in URL parameters, log files, config exports, and error reports. Inspecting what's actually inside them is essential for incident response, security audits, and figuring out whether sensitive data leaked. The decoded payload tells you what an attacker would have seen if they got hold of the token.
- Cross-team and cross-org communication Sharing 'here's the JWT my service is producing — what's the auth team expecting?' is far easier when both sides can paste it and see a structured view. The header, claim names, and timestamp format become the shared vocabulary.
- Security review and pen-testing Production JWTs often have findable problems — missing `exp`, weak HMAC secrets, sensitive PII in the payload, ancient `alg` choices. Decoding samples from production is the first step in any auth-stack security review.
- Onboarding and education JWTs are abstract until you actually pull one apart. New team members learning the auth flow benefit from seeing the three concrete parts, the base64url encoding, and the relationship between claims and verification. A decoder is the fastest path from 'JWT means signed JSON' to actually understanding.
What's inside header.payload.signature
A JWT is three base64url-encoded strings concatenated with periods. Each part is independent — the header tells you the algorithm, the payload contains the claims, the signature lets you verify the token wasn't tampered with. Critical to understand: base64url is encoding, NOT encryption. Anyone can decode the payload of any JWT.
- Part 1 — Header JSON object with metadata about the token. Always contains `alg` (the signing algorithm) and `typ` (almost always `JWT`). May contain `kid` (key identifier — tells the verifier which key to use when there are multiple), `x5t` (X.509 certificate thumbprint), `jku` (JWK Set URL — where to find the verification key). Base64url-encoded into the first part of the dotted string.
- Part 2 — Payload JSON object with the token's claims — both standard claims (iss, sub, aud, exp, nbf, iat, jti) and any application-specific data. This is what carries the actual authentication and authorization information. Base64url-encoded into the second part of the dotted string. NOT encrypted — anyone who sees the token can decode and read it.
- Part 3 — Signature Cryptographic signature (or HMAC) computed over `base64url(header) + '.' + base64url(payload)`. The third part of the dotted string. This is what makes the token tamper-evident — change any byte of the header or payload and the signature no longer verifies. Note: the signature only proves authenticity and integrity; it doesn't hide the payload.
- The crucial property: base64url is NOT encryption Don't store secrets in a JWT payload. Don't put passwords, private API keys, sensitive PII, or anything you wouldn't want a third party to read. The signature stops tampering; it doesn't stop reading. If you need confidentiality, you need JWE (encrypted JWTs) — a different format entirely — or you need to keep sensitive data on the server and only put an identifier in the JWT.
Every JWT signing algorithm, explained
RFC 7518 (JWA — JSON Web Algorithms) defines the standard set. Three categories: symmetric (HMAC family), traditional asymmetric (RSA family), and modern asymmetric (ECDSA, EdDSA). Plus one entry — `none` — that exists for completeness and has caused several catastrophic real-world CVEs.
- HS256 / HS384 / HS512 — HMAC with SHA-2 Symmetric (shared-secret) signing using HMAC over SHA-256, SHA-384, or SHA-512. Same key is used to sign and verify. Fast, simple, no key infrastructure required. The tradeoff: every party that needs to verify must also know the signing secret, which means any verifier can also forge tokens. Use when: a single service issues and verifies its own tokens (e.g., a session-management server). HS256 is the most common JWT algorithm in the wild — small payloads, simple deployment, just don't share the secret.
- RS256 / RS384 / RS512 — RSA with SHA-2 (PKCS#1 v1.5) Asymmetric (public/private key) signing using RSA with PKCS#1 v1.5 padding. The issuer signs with a private key; verifiers check with the corresponding public key. The most common asymmetric choice in production today. Keys are large (2048+ bits recommended); signatures are 256 bytes for RS256. The standard for distributed systems where you want anyone to be able to verify but only the issuer to be able to sign — OAuth 2.0 and OpenID Connect almost universally use RS256.
- PS256 / PS384 / PS512 — RSASSA-PSS with SHA-2 Modern RSA variant using probabilistic signature scheme (PSS) padding instead of PKCS#1 v1.5. Same key types as RS256 but cryptographically stronger (provably secure under stronger assumptions). Less universally supported in older libraries — pick RS256 for maximum compatibility, PS256 if you control both sides and want the better construction.
- ES256 / ES384 / ES512 — ECDSA with NIST curves Elliptic curve signing. ES256 uses NIST curve P-256, ES384 uses P-384, ES512 uses P-521. Much smaller keys and signatures than RSA at equivalent security — an ES256 key is ~32 bytes vs ~256 bytes for RS256. Faster too. The right modern choice when bandwidth or storage matters. Note: ES512 uses P-521 (yes, 521, not 512 — the curve was named for its prime field size).
- EdDSA — Edwards-curve DSA (Ed25519, Ed448) Most modern signing algorithm in the JWT spec. Uses Edwards curves (Ed25519 by default, Ed448 for higher security margin). Faster than ECDSA, smaller signatures, deterministic (no random-number generator quality risk), resistant to several side-channel attacks that ECDSA struggles with. The recommended choice for any new JWT implementation — if your stack supports it and you control both ends, prefer EdDSA over everything else.
- none — DANGEROUS, never accept Specifies that the token is unsigned. Defined in the JWT spec for the case where transport security alone is sufficient. In practice it's a notorious security hole: many libraries historically accepted `alg: none` tokens as valid by default, allowing trivial forgery (just construct any payload, base64url-encode it, leave the signature empty). Your verifier must explicitly allow-list the algorithms you accept — never trust the `alg` in the token itself to determine what counts as valid. See section 06 for the full vulnerability landscape.
The standard claims (and how to read them)
RFC 7519 defines seven 'registered claims' — short, well-known claim names with standardized meanings. Plus countless public and private claims used by specific applications and frameworks (OpenID Connect, OAuth 2.0, AWS, Auth0, etc. each add their own). The standard claims are what every JWT verifier should be checking.
- iss — Issuer Identifies the principal that issued the JWT. Typically a URL (`https://accounts.google.com`) or a domain. Verifiers should check that this matches the expected issuer — accepting tokens from arbitrary issuers is a common authorization mistake. In OAuth/OIDC, `iss` is paired with `sub` to form a globally unique user identifier.
- sub — Subject Identifies who the JWT is about — typically the user ID. Should be unique within the scope of the issuer. Common values: a UUID, a database user ID, an email address. Combined with `iss`, the pair `(iss, sub)` is the canonical global identifier for a user across systems.
- aud — Audience Identifies the intended recipients of the JWT. Can be a single string or an array. Verifiers MUST check that they're in the audience — accepting tokens not meant for you is a classic 'confused deputy' vulnerability. In OAuth/OIDC, `aud` is typically the client ID of the relying party.
- exp — Expiration time Unix timestamp (seconds since epoch). The JWT must not be accepted after this time. Verifiers must check it (the JWT spec marks `exp` as optional, but skipping the check is a top-tier vulnerability). Reasonable values: 15 minutes for access tokens, hours for ID tokens, longer for refresh tokens (which should typically be opaque, not JWTs).
- nbf — Not before Unix timestamp before which the JWT must not be accepted. Used for tokens that should activate at a future time. Less common than `exp` but defined in the same spec — verifiers should check it when present.
- iat — Issued at Unix timestamp of when the JWT was issued. Useful for: detecting impossibly-old tokens, rate-limiting, audit trails. Not used directly for validity (use `exp` for that). Some implementations require `iat` to be in the past; some reject tokens with `iat` in the future.
- jti — JWT ID Unique identifier for the JWT — typically a UUID. Used for: one-time-use tokens (server records the `jti` after first use), revocation lists, audit logging. The only standard claim that lets you implement revocation for JWTs (since JWTs are otherwise stateless by design).
- Public and private claims Beyond the seven standard claims, JWTs commonly carry: `name`, `email`, `email_verified`, `picture`, `roles`, `scope`, `groups`, `preferred_username`, `azp` (authorized party), `nonce`, and dozens more depending on the framework. OpenID Connect defines many of these in its own spec; OAuth scopes use `scope` or `scp`. Truly application-specific claims should use namespaced names (e.g., `https://my-app.example.com/role`) to avoid collisions.
The six ways JWTs go wrong in production
JWTs are one of the most consistently mis-implemented authentication mechanisms in modern web development. Each of the six pitfalls below has caused real CVEs and real breaches at companies you've heard of. Most stem from the same root cause: the JWT verifier trusting something it shouldn't.
- The `alg: none` attack JWT spec allows an unsigned token with `alg: none` and an empty signature. Historically, many libraries accepted these as valid. Attack: construct an arbitrary payload, base64url-encode it, set the header `alg` to `none`, leave the signature empty — you've forged a 'valid' token. Fix: your verifier must explicitly allow-list the algorithms you accept (e.g., `['RS256']`), never trust the `alg` in the token to decide what's valid. Modern libraries reject `none` by default; double-check yours.
- Algorithm confusion (HS256 ↔ RS256) A server uses RS256 (asymmetric, public key for verify) and a library that picks the verification algorithm from the token's `alg` header. Attack: an attacker takes the public key (which is, well, public), sets the token's `alg` to `HS256`, and signs with HMAC-SHA256 using the public key as the HMAC secret. The verifier reads `alg: HS256`, uses the public key as the secret (because that's what it has), and verifies. Famous CVEs across many libraries. Fix: hardcode the expected algorithm on the verification side — never let the token specify.
- Decoding without verifying Some applications base64-decode a JWT, parse the claims, and use them without checking the signature. This is exactly equivalent to having no signature at all — anyone can forge a token. Surprisingly common in microservice setups where the developer 'trusts' the upstream service. Fix: every JWT consumer must verify the signature before trusting any claim, every time.
- Skipping or ignoring `exp` The `exp` claim is marked optional in the JWT spec, and many verifiers don't check it strictly. Tokens issued years ago can remain valid forever if no one's checking. Fix: require `exp` on every token, reject tokens without it, reject tokens past it. Also check `nbf` if present and `iat` for sanity (reject tokens dated in the far future).
- Sensitive data in the payload Base64url is encoding, not encryption. The payload of every JWT is readable by anyone who has the token. Storing API keys, passwords, internal user IDs, sensitive PII, or anything else you wouldn't want a third party to see is a confidentiality breach waiting to happen. Fix: keep sensitive data on the server; put only a reference (a session ID, a user ID) in the JWT. If you genuinely need confidentiality in the token itself, use JWE (encrypted JWTs).
- Long-lived tokens with no revocation JWTs are stateless — once issued, they're valid until they expire. There's no built-in way to say 'this token is no longer valid'. If your user logs out, changes password, or has their account compromised, any JWT issued before that moment continues to work until `exp` passes. Fix: keep access-token lifetimes short (15 minutes typical), use refresh tokens for long-lived sessions (and store those server-side), maintain a `jti` blocklist if you need true revocation, or use opaque session tokens instead of JWTs when you need stateful revocation.
JWT vs JWS vs JWE vs JWK vs JWKS
Five acronyms that get used interchangeably and shouldn't be. They're related (all part of the JOSE — JavaScript Object Signing and Encryption — family of RFCs) but solve different problems. The short version: when someone says 'JWT', they almost always mean a JWS in compact serialization.
- JWT — JSON Web Token (RFC 7519) The umbrella term. A JWT is a compact, URL-safe means of representing claims between two parties. The claims are encoded as a JSON object that is either signed (JWS) or encrypted (JWE). 99% of what people call 'JWTs' in practice are JWS tokens in compact serialization.
- JWS — JSON Web Signature (RFC 7515) Defines the signing format. A JWS contains a header, payload, and signature. The signature provides integrity and authenticity — recipients can verify the token wasn't tampered with and was issued by the holder of the signing key. The dotted three-part format (`header.payload.signature`) is JWS in 'compact serialization'.
- JWE — JSON Web Encryption (RFC 7516) Defines the encryption format. A JWE has five parts (not three): protected header, encrypted key, IV, ciphertext, authentication tag. The payload is encrypted, so unlike JWS, recipients without the decryption key can't read the claims. Used when confidentiality is required. Much less common than JWS in practice — most applications keep sensitive data server-side rather than encrypting it into the token.
- JWA — JSON Web Algorithms (RFC 7518) Defines the algorithms used by JWS and JWE. Specifies HS256, RS256, ES256, EdDSA, and so on. When you see an algorithm name in a JWT header, it's from this spec.
- JWK — JSON Web Key (RFC 7517) Defines a JSON format for representing a single cryptographic key. Looks like `{ "kty": "RSA", "n": "...", "e": "AQAB", "alg": "RS256", "kid": "key-1" }`. Used as the building block for JWKS — JWK by itself is for representing one key in a standard way.
- JWKS — JSON Web Key Set (RFC 7517) A JSON document containing an array of JWK keys. Typically published at a well-known URL (e.g., `https://accounts.google.com/.well-known/openid-configuration`'s `jwks_uri`). Verifiers fetch the JWKS, find the key matching the token's `kid` header, and use it to verify the signature. The standard way to distribute public keys for JWT verification — supports key rotation (the issuer adds a new key alongside the old one, verifiers automatically pick up the new key).
- Compact vs JSON serialization JWS and JWE both define two serialization formats. Compact serialization is the dotted string most people recognize — `header.payload.signature`. JSON serialization wraps everything in a JSON object with named fields. Compact is what travels in HTTP headers and URLs; JSON is occasionally used in JSON-based protocols. This tool decodes compact serialization.
Use this programmatically
For programmatic JWT inspection — testing pipelines, security audits, log-processing scripts, monitoring. For privacy-sensitive use, prefer a local library (`jsonwebtoken` in Node, `pyjwt` in Python, `jose` in browsers) — your tokens never leave the process.
// Decode (no verification)
const res = await fetch('https://api.domainscan.in/v1/jwt/decode', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
})
});
const decoded = await res.json();
console.log(decoded.header.alg); // 'HS256'
console.log(decoded.payload.sub); // user ID
console.log(decoded.status.expired); // boolean
console.log(decoded.status.expiresIn); // seconds until exp
// Decode AND verify (provide the key)
const verified = await fetch('https://api.domainscan.in/v1/jwt/decode', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: 'eyJhbGciOiJIUzI1NiIs...',
key: 'your-hmac-secret',
algorithm: 'HS256' // explicit allow-list — never trust the token's alg
})
}).then(r => r.json());
if (verified.signature.valid) {
console.log('Token verified.');
} else {
console.error('Signature mismatch:', verified.signature.error);
}
// For privacy-sensitive use, decode locally — no network round-trip:
import { jwtDecode } from 'jwt-decode';
const payload = jwtDecode('eyJhbGciOiJIUzI1NiIs...');
console.log(payload);{
"success": "boolean",
"header": {
"alg": "HS256 | HS384 | HS512 | RS256 | ... | EdDSA | none",
"typ": "JWT | JWS | ...",
"kid": "string (optional — key identifier)",
"jku": "string (optional — JWK Set URL)",
"x5t": "string (optional — X.509 thumbprint)"
},
"payload": {
"iss": "string (optional — issuer)",
"sub": "string (optional — subject)",
"aud": "string | string[] (optional — audience)",
"exp": "number (optional — expiration unix timestamp)",
"nbf": "number (optional — not-before unix timestamp)",
"iat": "number (optional — issued-at unix timestamp)",
"jti": "string (optional — JWT ID)"
// ... any application-specific claims
},
"signature": {
"raw": "string (base64url-encoded signature bytes)",
"valid": "boolean | null (null if no key was provided for verification)",
"error": "string | null (verification error message, if any)"
},
"status": {
"expired": "boolean",
"expiresIn": "number (seconds; negative if expired)",
"notYetValid": "boolean (true if `nbf` is in the future)",
"age": "number (seconds since `iat`)"
}
}
// Request body
{
"token": "string (required — the JWT to decode)",
"key": "string (optional — HMAC secret or public key PEM for verification)",
"algorithm": "string (optional but recommended — expected algorithm to enforce)"
}How developers use JWT Decoder
Six patterns we see most often:
Your API call is rejected. Decode the token, check `exp` (expired?), `aud` (right audience?), `scope` or roles (right permissions?), `iss` (right issuer?). Three checks beat thirty minutes of staring at the auth code.
Many systems issue JWTs as API keys — like the sample on this page (`type: API-Key`). Decoding reveals when it was issued, when it expires, what scopes it has, and whether it leaks any internal IDs you didn't intend to expose.
Setting up OAuth or OpenID Connect for the first time involves a lot of 'why isn't this claim what I expected?' Decoding the tokens from your IdP is the fastest way to understand what the issuer is actually sending — vs. what the docs say it should send.
Paste a JWT plus the secret or public key. Confirm the signature verifies. Useful when integrating with a new issuer, when rotating keys, or when reproducing a 'token signature invalid' error that you're trying to track down.
JWTs found in URL parameters, request logs, error reports, or config exports. Decoding tells you what an attacker would have seen if they got the token, what its expiration was, and what user it represents — all key inputs for an incident response.
Showing a new team member 'this dotted string is actually three base64url-encoded pieces' is much faster with a visual decoder than with abstract explanations. The structure becomes concrete; the security concerns become tangible.
Common questions
- What is a JWT? A JSON Web Token — a compact, URL-safe way to represent signed (or encrypted) JSON claims between two parties. Structurally: three base64url-encoded parts (header, payload, signature) joined by periods. Used everywhere in modern auth: OAuth 2.0 access tokens, OpenID Connect ID tokens, API keys, signed URLs, magic links, session tokens, microservice authentication.
- Is base64url encryption? Can I store secrets in a JWT? No, and no. base64url is encoding, not encryption — anyone who sees a JWT can decode the payload and read every claim. The signature stops tampering; it doesn't stop reading. Never put sensitive data (passwords, private keys, sensitive PII) in a JWT payload. If you genuinely need confidentiality in the token, use JWE (encrypted JWTs) — a different format entirely. The usual better approach: keep sensitive data server-side, put only a reference (user ID, session ID) in the JWT.
- How do I verify a JWT signature? Three steps. (1) Determine the expected algorithm — hardcode it on your side, never trust the token's `alg` header. (2) Get the verification key — your HMAC secret for HS*, or the issuer's public key for RS*/ES*/PS*/EdDSA (typically fetched from a JWKS endpoint). (3) Run the verification using a battle-tested library (`jsonwebtoken` in Node, `pyjwt` in Python, `jose` everywhere). Never implement signature verification yourself; the failure modes are subtle and dangerous.
- What's the difference between JWS and JWE? JWS is signed JSON — the recipient can verify the signature but can also read the payload (anyone can). JWE is encrypted JSON — only the holder of the decryption key can read the payload. Both are 'JWTs' in the broad sense; in practice, ~99% of real-world JWTs are JWS. Use JWE only when you specifically need confidentiality in the token itself and can't keep sensitive data on the server.
- What's the difference between HS256 and RS256? HS256 is symmetric — the same secret signs and verifies. Fast and simple, but every verifier can also forge tokens. RS256 is asymmetric — a private key signs, a public key verifies. The issuer can mint tokens that anyone with the public key can verify, without giving any verifier the power to mint new tokens. Use HS256 when one service handles both ends; use RS256 (or ES256 / EdDSA for modern alternatives) for distributed systems.
- What is the "alg: none" attack and how do I prevent it? The JWT spec allows an unsigned token with `alg: none` and an empty signature. Many libraries used to accept these as valid by default, allowing trivial forgery — construct any payload, set `alg` to `none`, leave the signature empty, you have a 'valid' token. Prevention: your verifier must explicitly allow-list the algorithms you accept (e.g., `['RS256']`) and never trust the token's own `alg` header to decide what's valid. All modern libraries reject `none` by default; double-check yours.
- Can JWTs be revoked? Not directly. JWTs are stateless — once issued, they're valid until `exp`. There's no central state saying 'this token is no longer valid'. Workarounds: keep access-token lifetimes short (15 minutes typical) so revocation happens implicitly; use a `jti` blocklist on the server for true revocation; or use opaque session tokens (random strings looked up server-side) instead of JWTs when revocation is critical. For most cases, short lifetimes + refresh tokens is the right pattern.
- What's a JWKS endpoint? A JSON Web Key Set — a URL that publishes the public keys an issuer uses for signing JWTs. Verifiers fetch this URL, find the key matching the token's `kid` header, and use it to verify the signature. Example: `https://accounts.google.com/.well-known/jwks.json`. This is the standard way to distribute public keys for JWT verification and the mechanism that makes key rotation possible — the issuer adds a new key alongside the old one, verifiers automatically pick up the new key on their next fetch.