creatorvalet

JWT decoder

Decoding a JWT needs no key at all — that is the part people get wrong. Checking its signature needs one, and that happens here too, without the token ever leaving this page.

Runs in your browser0 bytes uploaded

Decoding happens in this tab. Nothing is uploaded, and the token is never put in the address bar, in history, or in storage.

The decoded header, payload and claims appear here. No key is needed for that — a JWT is readable by anyone holding it.

A token is a live credential, not a piece of data

That distinction is the whole reason this page exists. A spreadsheet you paste into a converter is a copy of something; a JSON Web Token is the thing itself. While it is unexpired, whoever holds those few hundred characters can act as you against whichever service issued it, without a password and usually without a trace. Pasting one into a web form is not like uploading a document — it is closer to reading your password aloud to a stranger who promised not to write it down.

Developers do this many times a day, often with a token minted against production, because the alternative is squinting at base64url in a terminal. The answer is not to stop looking at tokens. It is to look at them somewhere the question of trust never comes up.

Decoding needs no key — and almost everyone gets this backwards

A JWT is three segments separated by dots: header, payload, signature. The first two are JSON that has been rewritten in the base64url alphabet so it survives being carried in an HTTP header or a query string. Base64url is a substitution, not a lock. Reversing it takes one line of code in any language, requires no password, no key and no permission, and the result is exactly the JSON the issuer wrote.

People assume otherwise because base64 looks like ciphertext — a dense block of mixed-case letters and digits reads as scrambled to the eye, so the eye concludes it is secret. It is not. Every proxy the token passes through, every access log that records anAuthorization header, every browser extension with permission to read the page, and every colleague you send it to for debugging can read the claims as easily as this page just did. If you want the underlying mechanic on its own, thebase64 decoder shows the alphabet swap directly, and theJSON formatter handles the object once you have it.

What the signature buys you is integrity, not secrecy. It proves the claims have not been edited since they were issued. It does nothing to hide them, and it was never meant to. That is why a payload should carry an identifier and a scope, never a password, an API key or anything else you would not publish — and why this tool flags claim names that look like one, along with tokens that carry another token inside them.

What this JWT decoder tells you that a dump of the JSON does not

Most decoders print the two JSON objects and stop, which leaves you reading numbers.1626264000 is not an answer to any question a person actually has. The question is almost always “is this thing still good?”, so that is the first line on the receipt: expired three hours ago, or valid for another forty minutes, in words. The same treatment goes to iat and nbf, including the case where a token is not valid yet because the issuing server’s clock runs ahead.

Underneath that sit the findings. alg set to none is called out in red, because an unsecured token renders identically to a signed one and a decoder that shows both the same way conceals the single most dangerous property of its input. So does a header carrying jku, jwk or x5u, each of which lets the token nominate the key it will be checked against. A missing expgets a note, because a token with no expiry stays useful for as long as it stays leaked.

The input is cleaned up before any of this, and the tool says what it removed. PastingAuthorization: Bearer eyJ… straight from a network panel works. So does a quoted line lifted out of a JSON file with its trailing comma attached, and a token a terminal wrapped across three lines. Refusing those is technically correct and practically useless.

When it is not a token, the message says what it is

A string with two dots in it is not automatically a JWT, so the failure messages try to identify what you actually have. Five segments means a JWE — an encrypted token, whose contents no decoder can show without the decryption key, which is a real answer rather than a complaint. Two segments usually means the copy was cut short. One segment is an ordinary base64 string and gets pointed at the right tool. And if the token contains+, / or =, something re-encoded it as standard base64: the claims may still decode, but the signature will never verify no matter how correct your key is, because it was computed over different characters.

A close cousin is a token copied out of a query string with its percent-encoding still attached, so the dots arrive as %2E and there are no segments to find at all. Run it through the URL decoder first and paste the result back here. It is worth knowing which of the two happened, because they fail differently: percent-encoding is reversible and costs you nothing, while a re-encode to standard base64 has thrown away the exact bytes the signature was calculated over.

Checking the signature, without the usual trade

The standard advice is never to paste a key into a web page. That advice is aimed at pages that transmit it, and it is also too broad to be true: RS256 and ES256 are verified with the public key, which the issuer publishes at a URL anyone can fetch. Refusing to check those protects nothing at all. Only HS256’s shared secret genuinely deserves the caution, and even then the risk is the sending, not the typing.

So verification is offered here, and it is opt-in — decoding never asks for a key, because the most important thing this page can teach is that it does not need one. When you do ask, the work runs through crypto.subtle, the cryptography engine already compiled into your browser. HS256, RS256, PS256, ES256 and their larger variants are all supported, plus Ed25519 where the browser has it. The key can be a raw secret, aBEGIN PUBLIC KEY block, a single JWK, or an entire JWK Set copied straight from a /.well-known/jwks.json response — in which case the right key is picked by matching kid, which is the part that usually goes wrong after a rotation.

Two things it deliberately refuses. It will not verify alg: none, since there is nothing there to check and any answer would be misleading. And it will not check an HS256 token against a public key: that combination is the algorithm-confusion attack itself, where a token is switched from RS256 to HS256 and signed with the server’s own published key. Being accommodating there would make the tool a demonstration of the bug rather than a defense against it.

What it will not do, and why there is no encoder

A valid signature means one narrow thing: the claims were signed by the holder of the matching key and have not changed since. It does not mean the token is unexpired, that the issuer is the one you expected, that the audience names your service, or that the account still exists. Your application has to check all of that, and only your application knows the right values.

There is also no companion encoder here, and that is a decision rather than an omission. Signing a token means handing over the secret or the private key, and what comes out the other end is a working credential. Decoding asks for nothing; signing asks for precisely the material you should never paste anywhere. The search demand exists — it is a fortieth of the demand for decoding — and it is not enough to justify building a page whose output is a valid login. If you need to mint tokens, do it in your own code with your own library, where the key never has to travel at all.

Why nothing here can leave the page

Decoding is pure string manipulation: split on dots, map one alphabet onto another, hand the result to JSON.parse. No step in that sequence has anywhere to send something, even by mistake. Verification adds one call into the browser’s own crypto engine, which operates on bytes already in memory on your machine. Both are checkable rather than promised — open your network panel, paste a token, and watch the request list stay exactly as long as it was.

Press Verify and you will see precisely one new entry, which is worth explaining rather than glossing over: the signature code is not shipped with the page, so the first check fetches it. That request goes to this site for a JavaScript file and carries no part of your token or your key — it is the page downloading a tool, not uploading your input. Everything after it is local again, and a second check adds nothing at all.

One distinction is worth making, because it is the one that actually separates tools in this category. “We do not send your token” and “nothing on this page could send it” are different claims. A site that loads a third-party tag manager or analytics loader has granted an outside party the ability to run arbitrary code in the page after it loads, and whatever the site promised was true of the code it shipped, not of the code that ends up running. This page loads nothing from any other origin — no fonts, no analytics, no consent platform, no beacon. That is not a policy anyone has to take on faith; it is a property of the page you can read off the network panel in five seconds.

Questions

Is a JWT encrypted? Can other people read what is in mine?

They can read all of it, and they need no key or password to do it. The header and payload are base64url — an alphabet swap, reversible by anyone, chosen so the token survives being put in a URL or an HTTP header. Encoding is not encryption, and the two get confused constantly because base64 looks like ciphertext to the eye. What a signature gives you is integrity, not secrecy: it proves nobody altered the claims after they were issued, while leaving those claims perfectly legible to every proxy, log file and browser extension along the way. So treat a payload as public. A user id belongs there; an internal email address is a judgment call; a password or an API key never does, and this page flags claim names that look like one.

What does “alg”: “none” mean, and why does this page shout about it?

It means the token carries no signature and nothing about it can be proven. RFC 7519 defines these unsecured tokens for cases where the transport is already trusted, but the header is chosen by whoever wrote the token — so an attacker can take a real token, swap the algorithm to none, delete the signature, edit the claims to make themselves an administrator, and send it. Whether that works depends entirely on the receiving library refusing it, and a long line of them historically did not. That is why the finding is red rather than gray: on screen an unsecured token looks exactly like a signed one, and a decoder that renders both the same way hides the single most dangerous thing about the input.

Why will this tool not check an HS256 token against a public key?

Because doing so is the attack, not a convenience. HS256 is symmetric — the same shared secret both signs and checks. RS256 is asymmetric, signed with a private key and checked with a public one that anybody may download. The exploit joins them: take an RS256 token, change the algorithm to HS256, then sign it using the server’s published public key as if it were the shared secret. A verifier that reads the algorithm out of the token and obligingly follows it will accept the result, because it computes exactly the HMAC the attacker computed. Refusing that pairing costs a legitimate user one clear sentence and closes a hole that has been found in production authentication code more than once.

How can the signature be checked here without the token being sent anywhere?

Because the arithmetic runs in the tab. Decoding is pure string work — split on dots, swap the base64url alphabet, parse JSON — and there is no step in it that could involve a network even by accident. Verification uses crypto.subtle, the cryptography engine already built into the browser, so the HMAC or RSA operation happens on the same machine that holds the key. Both claims are checkable rather than promised: open the network panel, paste a token, and watch the request list stay empty. Worth knowing what a page cannot promise, though — a site that loads a third-party tag manager can have arbitrary code added to it after the fact, so “we do not send it” and “nothing here could send it” are different statements. This page loads nothing from anywhere else.

The signature says valid — does that mean I can trust the token?

It means one specific thing: the claims have not been altered since they were signed with the matching key. Everything else is still on you. A valid signature says nothing about whether the token has expired, whether the issuer is who you expected, whether the audience claim names your service, or whether the account was disabled ten minutes ago. Real verification checks the signature and then exp, nbf, iss and aud, and consults whatever revocation you have — this page reads those claims and tells you what they say, but only your application knows which values are the right ones. A token that is cryptographically perfect and three weeks past its expiry should be refused by anything correct.