Skip to content

JWT Decoder

Paste a JSON Web Token to read its header and payload, see each claim with its date and whether the token has expired, and get warnings about risky settings. The token never leaves your browser, and its signature is not verified.

Runs locally in your browser

Input

1 line · 325 characters
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS50ZXN0Iiwic3ViIjoidXNlcl8xMDI0IiwiYXVkIjpbImFwaS5leGFtcGxlLnRlc3QiXSwiaWF0IjoxNzY3MjI1NjAwLCJleHAiOjE3NjcyMjkyMDAsInNjb3BlIjoicmVhZDpvcmRlcnMgd3JpdGU6b3JkZXJzIiwiYWNjb3VudF9pZCI6MTIzNDU2Nzg5MDEyMzQ1Njc4OTB9.6u4rk7Ws6_3t5H480WC8pOT-lK9-pBt37-ASZ_QeGJo

Decoded

Signature not verified

Header

The header appears here

Payload

The payload appears here

Claims

The payload's claims appear here.

What the JWT decoder does

The decoder reads a JSON Web Token: it splits it into its parts, decodes the header and the payload from base64url and shows them as JSON, and lists every claim with its value as written. Times like exp get their dates in UTC and in your time zone, and a status says whether the token is active, expired or not yet valid, counting every second. Notes point out an unsigned token, headers that name their own keys, secrets in the payload and lifetimes of more than 30 days. It never checks the signature, and it says so on every token.

How to decode a JWT

  1. Paste the token. A Bearer prefix, quotes around it and line breaks from a wrapped token are set aside.
  2. Read the status beside Signature not verified: Active, Expired, Not yet valid or No expiry.
  3. Look at the header and the payload, and at Claims for each claim's meaning and date. Time display shows dates in UTC only, or in your time zone too.
  4. Copy the header, the payload or a single claim. If Problems lists one, select it to see its place in the token.

Examples

An HS256 token that expired in 2023

Input

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJpYXQiOjE2OTk5OTY0MDAsImV4cCI6MTcwMDAwMDAwMH0.VV63pGQEZPvcrCE-T8RwBleef2XGSfFqwDeHK9qOOIY

Output

Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"123","iat":1699996400,"exp":1700000000}
iat: 2023-11-14T21:13:20Z
exp: 2023-11-14T22:13:20Z
Status: Expired · Signature not verified
A token that is not signed at all

Input

eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjMifQ.

Output

Header: {"alg":"none"}
Payload: {"sub":"123"}
Status: No expiry · Signature not verified
A character that base64url does not have

Input

eyJhbGciOiJIUzI1NiJ9.eyJzdWIi*fQ.c2ln

Problem reported

Line 1, column 30: * is not a base64url character, in the payload. base64url has A–Z, a–z, 0–9, - and _ only. (JWT_BAD_BASE64URL)

How a JWT is built

A signed JWT, a JWS in the terms of RFC 7515 (opens in a new tab), has three parts joined by dots: header.payload.signature. The header is a JSON object that names the signing algorithm in alg, often with typ: "JWT" and a key ID in kid. The payload is a JSON object of claims, statements about the user or the session. Both are written in base64url, the URL-safe Base64 without = padding; the Base64 Encoder & Decoder shows how that alphabet works.

The signature is computed over the first two parts exactly as they are written, header.payload, with the issuer's secret or private key. Change one character of either and the signature no longer matches. That is the only protection a JWS has: its contents are readable by anyone.

An encrypted JWT, a JWE (RFC 7516 (opens in a new tab)), has five parts: a header, an encrypted key, an initialization vector, the ciphertext and an authentication tag. Its header can be read; its claims open only with the recipient's key, so the decoder shows the header and the enc algorithm and stops there.

Registered claims and NumericDate

RFC 7519 (opens in a new tab) defines seven claims with fixed meanings. iss names the issuer, sub the subject, usually a user ID, and aud the audience, the service or services the token is for: a string or a list. exp is when the token expires, nbf when it starts to be valid and iat when it was issued. jti is a unique ID, which lets a server refuse a token it has seen before. Any other claim, like scope or email, is up to the issuer.

The three times are NumericDates: seconds since 1970-01-01T00:00:00Z, without leap seconds, possibly with a fraction. A common mistake is to write them in milliseconds, as JavaScript's Date.now() returns them. 1700000000000 read as seconds lies in the year 55840, so the decoder flags it and shows what it would be as milliseconds. The Unix Timestamp Converter converts such values both ways.

The decoder compares exp and nbf with your clock to the second, with no tolerance. Servers usually allow a little clock skew, a few seconds up to a few minutes, so a token that has just expired here may still pass there, and one issued by a server whose clock runs ahead may look not yet valid for a moment.

Decoding is not verifying

Anyone can decode a JWT: the header and the payload are only base64url, not encrypted. What makes a token trustworthy is its signature, and checking it needs the key: the shared secret for HMAC algorithms like HS256, or the issuer's public key for RSA, RSA-PSS, ECDSA and EdDSA. That check belongs on the server that accepts the token, with a maintained JWT library.

A verifier should also decide which algorithms it accepts instead of taking alg from the token, and check iss, aud, exp and nbf as well as the signature. A token can be perfectly readable here and still be rejected there for any of these reasons. This decoder never verifies a signature, which is why every token, valid or not, shows Signature not verified.

Security pitfalls

RFC 8725 (opens in a new tab), the best current practice for JWTs, lists the mistakes that keep recurring, and the decoder notes the ones a token itself reveals:

  • alg: none declares an unsigned token. Libraries that once accepted it let anyone forge tokens; a verifier must refuse it.
  • Key headers: jku and x5u point to URLs to fetch keys from, jwk and x5c carry a key or a certificate inside the token. A verifier that trusts them lets the token choose the key it is checked with.
  • Secrets in the payload: a claim named password, api_key or ssn is readable by everyone who has the token, including logs and browser history.
  • Long lifetimes: a token valid for weeks stays usable long after a leak. Access tokens usually live minutes or hours, renewed with a refresh token.

Options

Options
OptionValuesDefaultWhen to change it
Time displayLocal and UTC, UTC onlyLocal and UTCUTC only when you compare with server logs, or share a screenshot that should not reveal your time zone.

Common errors and fixes

Common errors and how to fix them
CodeWhat it meansHow to fix it
JWT_MALFORMEDErrorThe text does not have 3 parts, or 5 for a JWE, or its header or payload is empty: often a token cut short when it was copied.Copy the whole token again; it ends after the signature, before any space or quote.
JWT_BAD_BASE64URLErrorA part has a character that base64url does not use, like = padding, + and / from standard Base64, or a space; or a length that cannot be base64url.Copy the token again; the marked character shows where it broke.
JWT_BAD_JSONErrorThe header or the payload decodes to something that is not valid JSON.The token was changed or built by hand; ask its issuer for a new one.
JWT_PART_NOT_OBJECTErrorThe header or the payload is valid JSON, but a string, a number or a list instead of an object.A JWT's header and payload are always objects; the token was not made by a JWT library.
JWT_JWE_UNSUPPORTEDWarningThe token is encrypted (five parts): its claims need the recipient's key.Decrypt it in the service that holds the key; only the header can be read here.
JWT_ALG_NONEWarningThe header says the token is not signed.Never accept it in production; make the verifier require the algorithm it expects.
JWT_EXPIREDWarningThe current time is past exp.Get a new token, or renew it with a refresh token.
JWT_NOT_YET_VALIDWarningThe current time is before nbf.Wait until nbf, or check the issuer's clock.
JWT_NO_EXPWarningThe payload has no exp claim, so the token never expires.Have the issuer add exp; a token without it stays valid until its key changes.
JWT_TIME_CLAIM_INVALIDWarningexp, nbf or iat is not a NumericDate: a string, milliseconds instead of seconds, a negative value or one outside every date.Write the time as a number of seconds since 1970-01-01T00:00:00Z.
JWT_LONG_LIFETIMENoteThe token is valid for more than 30 days between its iat and its exp.Shorten the lifetime and renew tokens more often.
JWT_UNTRUSTED_KEY_HEADERNoteThe header has jku, x5u, jwk or x5c: a key source chosen by the token itself.Verify with keys the server already trusts, never with keys the token brings.
JWT_SENSITIVE_CLAIMWarningThe payload has a claim named like a secret, such as password or api_key.Keep secrets out of tokens; send an ID and look the data up on the server.
JSON_DUPLICATE_KEYWarningThe header or the payload has the same claim twice; libraries disagree on which one counts.Remove the duplicate where the token is made; two readings of one token are a known attack.
JSON_NUMBER_UNSAFE_INTEGERNoteA claim is an integer larger than 2^53 − 1, which JSON.parse rounds.Nothing to fix here: the decoder shows it exactly. Issuers often write such IDs as strings.

Privacy

JWT Decoder runs entirely in your browser. What you type, paste or open is processed on your device: it is not sent to Toolzami, and Toolzami does not store it. Only the options you choose are remembered, in this browser.

Frequently asked questions

Is it safe to paste a real token here?

The token is decoded in your browser and never sent, stored or put in the URL. Still treat a live token like a password: it works for anyone until it expires. Prefer expired or test tokens, and revoke one you have shared.

Does it read tokens from Auth0, Okta, Cognito or Firebase?

Yes, their ID tokens and JWT access tokens are standard JWTs. Some services issue opaque access tokens instead, random strings without dots that only the issuer can look up; those are not JWTs and do not decode.

Last updated