The quick answer
You have a long string that starts with eyJ, an API keeps answering 401, and you need to know what is inside the token or when it stops working. Paste it into the JWT decoder. It splits the token, shows the header and payload as JSON and turns exp, nbf and iat into dates.
- Copy the token. A leading
Bearer, a wholeAuthorization:header, quotes and line breaks are fine, because the decoder strips them. - Paste it into the box. The three parts are colored, and every claim is listed, with a plain explanation for the standard ones.
- Read the badge: Expired, Not active yet, Not expired or No expiry set. The line under it says how long ago or how long from now.
- Optional, for HS256, HS384 and HS512 tokens only: type the signing secret in the signature section to check the signature. Use a test secret.
Where you paste a token matters
A JWT is a bearer token: whoever holds it can usually use it until it expires, much like a password. Decoding needs no server, because the first two parts are only encoded text, so a page has no reason to send your token anywhere, and a page that does has a copy of it.
To check a decoder, open your browser's developer tools, go to the Network tab, paste a test token and watch: no request should carry it. This decoder runs in your browser and does not send, store or log the token or a secret. Even so, prefer a test or expired token, and treat a live one pasted into a page you did not trust as leaked. Is it safe to upload a PDF to a free website applies the same check to documents.
The three parts of a JWT
A JSON Web Token is three pieces joined by dots: header.payload.signature. This example was signed with the secret demo-secret and expired in 2023, so it is safe to practice on. The badge will say Expired, and the signature check passes when you type the secret:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VyXzEwNDIiLCJhdWQiOiJhcGkuZXhhbXBsZS5jb20iLCJpYXQiOjE3MDAwMDAwMDAsIm5iZiI6MTcwMDAwMDAwMCwiZXhwIjoxNzAwMDAzNjAwfQ.NbgZZgo5WcMukX4ayCjaI_rObdQBfAg_OuWl8nqHoh4
| Part | Decodes to | What it is for |
|---|---|---|
| Header | {"alg":"HS256","typ":"JWT"} | How the token was signed |
| Payload | The claims as JSON: iss, sub, aud, iat, nbf and exp here | What the token says |
| Signature | 32 bytes of binary data, not text | Lets a server detect any change |
Each part is Base64URL: the Base64 alphabet with - and _ in place of + and /, and no = padding. That is an encoding anyone can undo, not encryption, and it is why most tokens start with eyJ, which is {" followed by a letter in Base64. Base64 encoding and decoding explained shows the mechanics, and the Base64 decoder reads any single part. A token with five parts is an encrypted one (JWE), and only its header can be read without the key.
The standard claims
A claim is one key in the payload. Seven are registered in the JWT standard (RFC 7519), and the decoder explains each one:
| Claim | Meaning |
|---|---|
iss | Issuer: who created and signed the token |
sub | Subject: who it is about, often a user ID |
aud | Audience: who it is for, a string or a list |
exp | Expiration: not valid at or after this moment |
nbf | Not before: not valid until this moment |
iat | Issued at: when it was created |
jti | Token ID: unique to this token |
Apps add their own claims, such as a role or an email address. The decoder also explains auth_time, nonce, azp, scope and sid. For a payload with long nested objects, the JSON formatter can re-indent or check it, and format and validate JSON covers what to do when it will not parse.
Reading exp, nbf and iat
These three are a number of seconds since 1 January 1970 UTC, so each is one moment, the same everywhere. In the example, iat is 1700000000 and exp is 1700003600. The difference is 3,600 seconds, so the token lives for one hour: 22:13:20 to 23:13:20 UTC on 14 November 2023, which is 5:13:20 PM to 6:13:20 PM in New York (EST). For other time zones, or a number from a log, use the Unix timestamp converter. A token is expired once the current time is at or after exp.
One trap: a 13 digit value is milliseconds, and JWT does not use milliseconds. Read as seconds, 1700003600000 would be the year 55840. The decoder warns about values that large instead of showing a date.
Decoding is not verifying
Anyone can write a header and a payload that decode perfectly with any claims they like. Only the signature, checked with the right key, separates a genuine token from a forged one. So read a decoder's result like this:
| What you see | What it shows | What it does not show |
|---|---|---|
| The token decodes | The text is well formed | Anything about who made it |
| Not expired | Your clock is before exp | That the token is genuine |
| Signature matches (HS256, typed secret) | That secret signed these exact bytes | That exp, iss and aud are acceptable |
alg is none | The token claims to be unsigned | Anything. Never accept it as proof |
A server that accepts tokens has to verify the signature with its own key and choose the algorithm itself, because libraries that trusted the token's alg value have accepted forged tokens. It must also check exp, nbf, iss and aud. Use a decoder to understand a token while debugging, never to decide who may do what.
The decoder checks a signature only for HS256, HS384 and HS512, which use a shared secret, and only when you type it. RS256 and ES256 need the issuer's public key, which this page does not take, so it says the signature was not checked. Type only secrets from a test setup: whoever has a production signing secret can mint tokens.
Anyone who holds an HS256 token can test guesses of its secret offline, so a short secret is a real hole. RFC 7518 requires a key at least as long as the hash output, 256 bits for HS256. That is about 40 random characters from a set of 85 to 94 symbols, and make a strong password shows how to get them.
Common errors and what to check
Wording differs between libraries, but the causes do not.
| Error | Likely cause | Look at |
|---|---|---|
| Token expired | exp has passed, or the checking machine's clock runs ahead | The Expires date. Get a fresh token, because editing exp breaks the signature |
| Not yet valid | Clock skew: the issuer's clock runs ahead of the checker's by seconds or minutes | nbf and iat against your clock. Many libraries allow a few seconds of leeway |
| Invalid audience | Issued for another API or client | aud against what your service expects |
| Invalid issuer | From another environment or tenant | iss, character for character, including a trailing slash |
| Invalid signature | Wrong secret, an altered token, or a Base64 secret read as text | Type the secret and try the Base64 option |
| Malformed token | A cut-off copy or a stray character | The part count: 3 signed, 5 encrypted |
Where to go next
To find tokens that leaked into a log file, the pattern \beyJ[\w-]+\.[\w-]+\.[\w-]* matches the three part shape. It is a starting point, so check the hits, and see how to test a regular expression first. HS256 signatures are built on the SHA-256 hash, and how to verify a download with a hash explains what a hash is and what a matching one proves.