π« JWT Token Decoder
Paste your token below β everything runs locally in your browser. Nothing leaves your device.
JSON Web Tokens are everywhere. They secure REST APIs, authenticate single-page applications, carry OAuth 2.0 access grants, and shuttle identity claims across microservices at millions of requests per second. But for all their ubiquity, JWTs are remarkably opaque at a glance β a dot-delimited wall of Base64url text that tells you nothing until you actually crack it open. Knowing how to read a token is not just a debugging convenience; it is a prerequisite for reasoning correctly about your authentication layer.
The Three-Part Structure You Actually Need to Understand
A JWT is three Base64url-encoded segments joined by literal period characters. The format is header.payload.signature, and each segment has a distinct purpose that shapes how you should think about the token's security properties.
The header is a small JSON object that declares the token type and the cryptographic algorithm used to produce the signature. A typical header looks like {"alg":"HS256","typ":"JWT"}. More complex tokens add a kid (key ID) field so the verifying party knows which of several possible keys to use β a pattern common in multi-tenant identity providers and JWKS-backed systems. The alg field matters enormously from a security standpoint, which is why decoders surface it prominently.
The payload carries claims β assertions about the subject, its permissions, and the token's validity window. Claims come in three flavors. Registered claims are a set of standardized shorthand names defined in RFC 7519: iss (issuer), sub (subject), aud (audience), exp (expiration time), nbf (not before), iat (issued at), and jti (JWT ID). Public claims are collision-resistant names intended for broad interoperability. Private claims are anything an application defines for its own internal use β roles, feature flags, tenant IDs, plan tiers, whatever the application cares about.
The signature is what separates a JWT from a plain JSON envelope. For symmetric algorithms like HS256, it is an HMAC computed over base64url(header) + "." + base64url(payload) using a shared secret. For asymmetric algorithms like RS256 or ES256, the issuer signs with a private key and recipients verify with the corresponding public key. The signature ensures the token has not been tampered with in transit β but it says nothing about whether the claims are true or whether the token has been revoked. Those are validation concerns, and they are entirely separate from decoding.
The alg=none Vulnerability That Still Breaks Things in 2024
One of the more alarming chapters in JWT's history is the alg:none attack. Early JWT libraries trusted the algorithm specified in the header when verifying signatures. An attacker who obtained a valid token could change "alg":"HS256" to "alg":"none", strip the signature segment (leaving the trailing dot), and some libraries would accept the result as cryptographically valid β because "no algorithm" means no signature check is needed. The library was doing exactly what the spec said; the flaw was in trusting the header blindly.
Modern libraries reject alg:none by default and require callers to explicitly allowlist acceptable algorithms. But the lesson generalizes: never let the token tell you how to verify itself. Allowlist the algorithms your application expects and reject anything else. When you decode a token and see alg:none in the header, it should be treated as a red flag, not a valid identity assertion.
Reading Timestamp Claims Correctly
Timestamp claims in JWTs β exp, nbf, and iat β are stored as NumericDate values: seconds since the Unix epoch (January 1, 1970, 00:00:00 UTC). This is not milliseconds. A common mistake is feeding these numbers directly into JavaScript's new Date() constructor, which expects milliseconds, producing dates 1000x further in the future than intended. The correct conversion is new Date(claimValue * 1000).
The exp claim defines the moment after which the token must be considered invalid. RFC 7519 specifies that verifiers should allow a small clock skew β typically up to a few minutes β to account for legitimate timing differences between issuer and verifier clocks. The nbf claim works in the opposite direction: it specifies the earliest time at which the token becomes valid. This is useful for tokens issued in advance that should not be usable immediately, like a link embedded in a scheduled email.
The iat claim records when the token was minted. It is informational rather than security-critical in most implementations, but some systems use it to enforce maximum token age separately from the exp claim β for example, requiring that access tokens be no more than one hour old regardless of what exp says.
Symmetric vs. Asymmetric: Choosing the Right Algorithm Family
The choice between HMAC-based algorithms (HS256, HS384, HS512) and public-key algorithms (RS256, ES256, PS256, EdDSA) has architectural implications beyond raw security. Symmetric algorithms use a single shared secret for both signing and verification. This means any service that needs to verify tokens also needs access to the secret β which creates key distribution problems at scale. If you have five microservices all verifying JWTs, all five need the secret, and a compromise of any one of them exposes the key that can forge tokens for everything.
Asymmetric algorithms solve this by splitting the key. The issuer keeps the private key and uses it to sign. Everyone else uses the public key β which can be published openly via a JWKS endpoint β to verify. A compromised verifier cannot forge tokens because it never had the signing key. This is why identity providers like Auth0, Okta, and AWS Cognito default to RS256 and publish their public keys at well-known JWKS URLs.
ES256 (ECDSA with P-256) produces significantly shorter signatures than RS256 while maintaining comparable security. EdDSA (Ed25519) is faster still and avoids some of the pitfalls of ECDSA implementation (particularly the requirement for a secure random nonce β a constraint that famously caused the PlayStation 3 signing key compromise). If you are building a new system with algorithm flexibility, EdDSA is worth considering.
What Decoding Cannot Tell You
Decoding a JWT reveals its claims and algorithm, but it cannot answer several questions that matter operationally. It cannot tell you whether the signature is valid β you need the secret or public key for that. It cannot tell you whether the token has been revoked β you need to consult a revocation list, blacklist, or check token family state. And it cannot tell you whether the claims are true β an issuer could put false information in the payload and the token would decode and verify just fine.
This distinction becomes critical when debugging authentication failures. A token might decode perfectly β correct-looking claims, non-expired timestamp, valid algorithm β and still be rejected by your server. The rejection could be a signature mismatch (wrong issuer key), audience validation failure (aud does not match the server's expected audience), or a revocation check. Decoding gives you the starting point for that investigation, not the final answer.
The Payload is Not Encrypted β Act Accordingly
JWT payloads are encoded, not encrypted. Base64url is trivially reversible by anyone who holds the token. This means anything you put in the payload is readable to the token bearer and to anyone who intercepts the token in transit. Do not store sensitive data β passwords, credit card numbers, SSNs, PII beyond what is strictly necessary β in a standard JWT payload.
If you need encrypted tokens, the spec covers that too: JWE (JSON Web Encryption) wraps the payload in an encrypted envelope. JWE tokens look similar to JWT at a glance but have five segments rather than three. Decoding them requires the decryption key. For most applications, TLS in transit combined with careful claim minimization is sufficient β put an opaque user ID in sub and look up sensitive attributes in your database when needed, rather than materializing everything into the token.
Understanding what you are looking at when you decode a JWT β the algorithm choice, the claim semantics, the timestamp arithmetic, the distinction between decoding and verification β transforms a token from an opaque black box into a readable, auditable security artifact. That clarity is worth developing deliberately rather than stumbling through when something breaks at 2am.