json

FormatValidateConvert

JWT Decoder

Verify signature

—

Paste a token on the left, or load the example.

About the decoder

JWT Decoder: read and verify a JSON Web Token.

Your document never leaves the browser.

jwt decoder · decode · verify · nothing leaves the browser

Paste a token to read its header and payload, see its claims with real times, and check its signature against a secret or a public key — in this tab, with nothing sent, stored or put in the address. Want to work on the payload itself? Open payload in the editor, or open the JSON editor and paste it there.

Three parts, readable by anyone

1

Token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1LTEyMyIsIm5hbWUiOiJBZGEiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAwMzYwMH0.bm90LWEtcmVhbC1zaWduYXR1cmU
2

Header

{
  "alg": "HS256",
  "typ": "JWT"
}
3

Payload

{
  "sub": "u-123",
  "name": "Ada",
  "iat": 1700000000,
  "exp": 1700003600
}

A JSON Web Token in its usual compact form is three pieces of base64url joined by dots. The first is a small JSON header naming the signing algorithm, the second is the payload of claims about a user or a session, and the third is the signature over the first two. The field above colours the three so you can see where each starts, and the Bearer prefix copied out of an Authorization header along with the token is stripped for you.

The thing most worth knowing is that the first two parts are encoded and not encrypted. Anyone who holds the token can read every claim in it, which is exactly what this page does, with no key at all, so never put a secret in a payload. And reading a token proves nothing about it: a forged token decodes as happily as a genuine one, and only the signature, checked against a key you trust, tells the two apart.

Checking the signature

The Verify card takes the algorithm from the header and checks the signature with the Web Crypto API built into the browser. Thirteen algorithms are supported: HS256, HS384 and HS512 with a shared secret; RS256, RS384 and RS512, and the PSS variants PS256, PS384 and PS512, with an RSA public key; ES256, ES384 and ES512 with a P-256, P-384 or P-521 key; and EdDSA with an Ed25519 key.

For an HS token, paste the secret as text, or tick Secret is base64url when your configuration stores it that way. For the others, paste the public key as a PEM block (PUBLIC KEY or RSA PUBLIC KEY), as the X.509 certificate that carries it, as a JWK, or as a whole JWKS from your provider’s jwks_uri, from which the key whose kid matches the header is chosen. A certificate is used for its key only; its dates and its issuer are not checked. The answer is live: it updates as you type, and reads Signature verified, Invalid signature, Not signed, Can't verify or Key problem, with the key the page used named beside it.

Algorithm confusion, and why it is refused

1

Token

eyJhbGciOiJub25lIn0.eyJzdWIiOiJBZGEifQ.
2

Reported

Not signed
This token declares alg "none", so it has no signature to verify.

The best-known JWT flaw is simple. A server that verifies RS256 tokens holds a public key, and public keys are public. If an attacker changes the header to HS256 and signs a forged payload with HMAC, using the public key’s bytes as the secret, a careless verifier that lets the token choose its own algorithm will compute the same HMAC and accept the forgery.

This page will not do that. The algorithm comes from the header, but the key has to be the kind that algorithm uses, so an HS token checked against an RSA or EC public key is refused as a Key problem whose message ends Verifying an HMAC token with a public key is the 'algorithm confusion' attack, so it is refused. A JWK that declares an alg of its own which differs from the header’s is refused as well.

A key inside the token is never used

The header may carry a key of its own: a jwk member, a certificate chain in x5c, or a link to one in jku or x5u. A verifier that trusts those has let the token vouch for itself, since whoever forged the token chose the key too. They are shown in the header like any other member and never used to verify, and a notice says so: The header carries its own key. It is not used to verify, since a token cannot vouch for itself. Following jku or x5u would also mean fetching a URL, and this page makes no requests.

Weak keys still verify, with a warning

RFC 7518 asks for an HMAC secret at least as long as the hash, 32 bytes for HS256 (§3.2), and for an RSA modulus of at least 2,048 bits (§3.3). A key below either line is still used, since this is a tool for reading tokens and not a gate, but the verdict comes with a notice such as The HMAC secret is 19 bytes; HS256 recommends at least 32. That is the notice the Example button shows, because the secret every JWT tutorial uses is shorter than its own algorithm recommends.

Expiry, not-before and issued-at

1

Token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1LTEyMyIsImlhdCI6MTcwMDAwMDAwMCwibmJmIjoxNzAwMDAwNDAwLCJleHAiOjE3MDAwMDM2MDB9.aW52ZW50ZWQtc2lnbmF0dXJl
2

Read at 2023-11-14 22:30 UTC

Active, expires in 43 minutes
iat · Issued at · 2023-11-14T22:13:20Z · 16 minutes ago
nbf · Not before · 2023-11-14T22:20:00Z · 10 minutes ago
exp · Expires · 2023-11-14T23:13:20Z · in 43 minutes

The example reads the token at 2023-11-14 22:30 UTC, so its relative times stay exact.

The Claims card lists every member of the payload, and names the registered ones and the OpenID Connect ones. The times, exp, nbf, iat and auth_time among them, are shown in UTC to the second with how long ago or how far off each is; hover one to see it in your own time zone. Above the table a status line gives the verdict on the times in one of four shapes, such as Expired 3 hours ago, Not valid until a stated time, Active, expires in 12 minutes, or No expiry: the token has no exp claim. It keeps itself current while the tab is open.

No clock skew is allowed. Servers often accept a token for a minute or two after its exp, to forgive clocks that disagree, but that allowance is your server’s setting, not a property of the token, so this page reports the plain arithmetic and leaves the leeway to you. A claim that breaks its own rules, an iat in the future or an iss that is not a string, is marked in the table in its own words.

Encrypted tokens

1

Token

eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..aW52ZW50ZWQtaXY.aW52ZW50ZWQtY2lwaGVydGV4dA.aW52ZW50ZWQtdGFn
2

Reported

Encrypted (JWE): only the header can be read

A token with five parts rather than three is a JWE, an encrypted token. Its header is plain base64url and is shown in full, naming the key-management and content-encryption algorithms. Everything else is ciphertext, and without the recipient’s private key there is nothing more to read.

What stays on your machine

A token is a credential: whoever holds an unexpired one can usually act as its owner. So the token and the key you paste are held only in this tab’s memory. They are not written to local or session storage, not placed in the address, not added to your history, and a link cannot arrive with a token already filled in. Reload, and both fields are empty again. Two things fill them for you: Try it in the decoder on a Guide page, and Open in JWT decoder in the editor’s status bar when a pane holds nothing but a token. Each hands its token across through the session storage of the tab you pressed it in, where the decoder reads it once and deletes it, so no link from anywhere else can do the same.

There is one exception, and it is yours to trigger. Open payload in the editor hands the decoded payload, never the token or the key, to the JSON editor through a one-shot record in this tab’s session storage, which the editor reads and deletes as it opens. From there you can see the claims as a tree, a table or a graph, or check their shape against a contract on the JSON Schema validator.

Never paste a production private key here or into any web page. You only need the public half to verify, and if you paste a private key by mistake the page uses its public half and warns you in the engine’s own words: Never paste a private key into a web page; only the public key is needed to verify.

FAQ

Frequently asked questions

Didn’t find your answer?Write to us on the contact page →
Is my token sent anywhere?

No. Decoding is base64url and a JSON parse, and verifying uses the Web Crypto API that every current browser ships, so both happen on your own machine and the page makes no request while you use it.

Why does it say “Can't verify”?

Because the token is one this page can read but cannot check. An encrypted token (JWE) is the usual reason, and the detail line then reads Encrypted tokens (JWE) can't be verified here, only decoded. The others are an algorithm outside the thirteen listed above, a header that marks a parameter as critical which the page does not understand, the rare unencoded-payload form, and a browser too old to offer Ed25519. Each one is named in the detail line under the title.

Why is my HS256 token refused with an RSA key?

HS256 is checked with a shared secret, and an RSA key is a public key. Using the bytes of a public key as an HMAC secret is exactly the forgery described in the algorithm confusion section, so the page reports Key problem and names the attack rather than computing a result. Paste the secret the token was signed with, or check which algorithm your issuer really uses.

Does decoding a token mean it’s valid?

No. A token that decodes cleanly was merely well-formed; anybody can write a header and a payload and base64url them. Validity needs the signature to check out against the right key, and then the times, the issuer and the audience to be ones your service accepts.

Can I create or sign tokens here?

No, and on purpose. Signing needs the private key or the shared secret, and a web page is the wrong place to type either. Mint tokens in your own code or your identity provider, and bring them here to be read.

Keyboard shortcuts

Send feedback

Questions, bug reports and feature requests are all welcome. A bug report is easiest to act on with the shape of the document that caused it — never send anything confidential.

Email us

Contact page, in a new tab, so this page stays as it is.

Settings

Indent

The result is written with it — YAML and XML at 2 spaces when it is Tab.

Code text size
14 px

Both panes.

Wrap long lines