JWT Decoder

Decode a JWT and inspect its header, claims and expiry.

Encoded token
Decoded
Claim reference

The three parts of a JWT

A JSON Web Token is three Base64URL-encoded segments separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiIxMjM0NTY3ODkwIn0 . SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
└──────────── 1. header ────────────┘   └────── 2. payload ──────┘   └────────── 3. signature ──────────┘
  • Header — the signing algorithm and token type, e.g. {"alg":"HS256","typ":"JWT"}.
  • Payload — the claims: statements about the subject plus registered fields such as exp, iat, iss.
  • Signature — HMAC or a digital signature over the first two segments, computed with a key the verifier holds.

Encoded is not encrypted

This is the single most important thing to understand about JWTs, and the source of most JWT security incidents. The payload is Base64URL-encoded, not encrypted. Anyone who obtains the token can read every claim in it in about one second — this tool does exactly that, and so does any command line:

echo 'eyJzdWIiOiIxMjM0NTY3ODkwIn0' | base64 -d
# {"sub":"1234567890"}

The practical rule: never put a secret in a JWT payload. No passwords, no API keys, no personally sensitive data. The signature proves the token was not tampered with; it does nothing to make the contents confidential. If you need confidentiality, use JWE (JSON Web Encryption), which is a separate and much less commonly used specification.

Signature verification vs. decoding

Decoding is pure string manipulation — anyone can do it. Verification requires the key and is the part that actually provides security. This tool deliberately does not verify signatures, for two reasons: verifying would require you to paste your secret or public key into a web page, and verification is something your server must do, in your own code, with a library you trust.

The server-side rule is absolute: always verify the signature before reading any claim. A token whose signature has not been verified is an unauthenticated user input, exactly like a form field.

The alg field is an attack surface

Three historic JWT vulnerabilities all come from trusting the header:

alg: none

Vulnerable libraries accepted tokens declaring no algorithm and no signature — so an attacker could forge any claims. Modern libraries reject none by default, but if you are maintaining older code, confirm that yours does.

Algorithm confusion (RS256 → HS256)

If a server signs with RS256 (asymmetric: private key signs, public key verifies) but does not pin the expected algorithm, an attacker can send a token declaring alg: HS256 and sign it with the public key — which is public. A library that trusts the header will HMAC-verify using that public key and accept the forgery. The fix is to pass the expected algorithm explicitly to your verification call, never to infer it from the token.

Weak HMAC secrets

HS256 uses a shared secret. Short or guessable secrets are brute-forceable offline, at no rate limit, once an attacker has one valid token. Use a random secret of at least 32 bytes, and prefer asymmetric RS256/ES256 when the verifier does not need to sign.

Claims you should always check

Decoding tells you what a token says; your server decides what it means. Beyond the signature, check at minimum:

  • exp — reject expired tokens. Also decide your policy on tokens with no exp: most implementations should reject them outright, because a token that never expires cannot be contained.
  • nbf — reject tokens not yet valid.
  • aud — reject tokens intended for a different service. Skipping this lets a token issued for one API authenticate against another.
  • iss — reject tokens from an issuer you do not trust.
  • role / scope — treat as data, not as authorisation. Authorisation decisions belong in your own database or policy layer; a role claim is only as trustworthy as your issuer, and stale roles are a common privilege-escalation path.

Expiry is not revocation

A JWT is self-contained and stateless, which is exactly why it is also hard to revoke. Once issued, a valid token remains valid until it expires — logging out does not invalidate it. This is the fundamental trade-off of stateless auth.

The standard mitigations are short expiry (5–15 minutes for access tokens) combined with refresh tokens that are stored server-side and can be revoked; or a revocation list keyed on jti checked on every request, which costs you the statelessness benefit but gives you real logout.

Where to store a JWT in the browser

Both common options are imperfect, and the "right" answer depends on your threat model:

  • localStorage — survives reloads and is simple, but any XSS on your site reads it. Given how easy XSS is to introduce through a dependency, this is a real risk.
  • HttpOnly; Secure; SameSite cookie — inaccessible to JavaScript, so XSS cannot steal it, but you must handle CSRF protection.

For most applications, short-lived access tokens in memory plus a refresh token in an HttpOnly cookie is the safest practical pattern. What you should never do is store a long-lived JWT in localStorage.

Frequently asked questions

This decoder runs entirely in your browser — the token is never transmitted or stored. That said, anyone who has the token can already use it, so treat any live token as a credential: if it is production and sensitive, rotate it after inspecting it.

No, and no web-based tool should. Verification requires your secret or public key, which must never leave your infrastructure. Decode here to inspect; verify in your own server code with a trusted library.

Check the clock on the machine that issued it. Expiry is an absolute Unix timestamp, so clock skew between the issuer and the verifier of even a minute can reject a freshly issued token. Most libraries allow a small leeway — usually 30–60 seconds.

No. It is Base64URL-encoded, which is trivially reversible by anyone. The signature prevents tampering, not reading. Never put secrets in a payload. For genuine confidentiality you need JWE.

It declares the token is unsigned. Vulnerable libraries accepted such tokens, letting anyone forge arbitrary claims. Always pass the expected algorithm to your verification function instead of trusting the token header.

You cannot invalidate a token that has already been issued. Use short-lived access tokens with revocable refresh tokens, or check a server-side revocation list keyed on the jti claim.