Search tools

Find and open any DevTooKit tool. Use arrow keys to navigate.

Home / Guides

JWT decode is not signature verification

Sumit Jha · Updated 2026-11-06

A compact JWT is three Base64url parts: header, payload, and signature. Decoding only reverses the first two. Anyone can change the payload and leave a garbage signature, and a decoder will still print the new claims. Trust requires checking the signature with the right key and algorithm, then checking exp, iss, and aud yourself.

Open JWT Decoder

What the three parts are

The compact serialization is header.payload.signature. The header and payload are JSON objects encoded with Base64url, which is Base64 with a URL-safe alphabet and often no padding. The signature is not JSON. It is a MAC or a public-key signature over the ASCII string header.payload — the encoded parts, not the raw JSON.

That last detail is why “I edited the JSON and recomputed nothing” still decodes. The decoder splits on dots, Base64url-decodes part one and part two, and JSON.parses them. It never hashes the signing input.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.signature

What a decoder can honestly tell you

You can read alg and typ from the header, and claims such as sub, exp, iat, iss, and aud from the payload. You can see that exp is a NumericDate in seconds, not milliseconds. You can see that the token is expired relative to your clock. None of that is authentication.

alg is attacker-controlled if you have not verified the signature. A token that says HS256 might be unsigned, or signed with a different algorithm, or signed with a key you do not have. Do not branch on the header before verification. Libraries that verify should be given the algorithm you expect, not the algorithm the header claims.

What verification needs

HMAC algorithms (HS256, HS384, HS512) need the shared secret. The signature is HMAC(secret, ascii(header) + "." + ascii(payload)). RSA and EC algorithms need the public key that matches the signer’s private key. A decoder page does not have that key, and it should not ask you to paste a production secret just to “see” claims.

After the signature matches, you still check time and audience. exp in the past means the token is stale. A missing aud check is how a token minted for one API gets replayed against another. Verification is a function you run on the server that holds the key. The DevTooKit decoder is for reading a token you already have while you debug.

Where the token goes

On the DevTooKit JWT page the split and the JSON parse happen in your browser. That avoids pasting a live access token into a third-party service, which is a real leak even when the decoder is honest. It does not make the decoded claims true. If you need a test token, use a non-production secret in the signer, then decode the result to confirm the claims you put in.

A check you can do with a test token

Mint a token with a throwaway secret, for example the string dev-only, and the claims sub and exp. Decode it and confirm those two claims match what you typed. Then change one character in the payload segment and decode again. The JSON changes, and the signature segment is untouched. That is the whole demonstration: the page is a parser.

To see verification, use a library on your machine with the same secret and HS256, and pass the algorithm in from your config rather than from the header. Flip a character in the payload and the library must reject it. If it still accepts the token, you are decoding in production too. Also compare exp with a clock you trust. A token that is “expired” in the decoder and accepted on a server is usually skew, not a broken signature.

Do not paste a production access token into a browser on a shared computer just to finish this check. The decode is local, and the secret still sits in the tab, in extensions, and in any crash log the browser keeps. Use a token you can revoke.

When you document this for someone else, write the two operations as different verbs. Decode means “print the JSON.” Verify means “check the MAC or signature with a key I chose, then check exp, iss, and aud.” A pull request that only adds a decode step in the request path is not authentication. The header’s alg is an input to that check only after you have decided which algorithms you accept. If the list is HS256, an RS256 token is a failure even when the payload looks right. The DevTooKit page will still show you that payload, which is useful while you are debugging and irrelevant to the allow-list.

FAQ

Does a successful decode mean the JWT is valid?

No. Decode only prints header and payload. Validity is a signature check plus exp, iss, and aud checks against keys you control.

Why can I edit the payload and still decode it?

Because the payload is just Base64url JSON. The signature is a separate part and is not consulted by a decoder.

Related tools

  • JWT SignerSign JWTs with HS256 / HS384 / HS512 using the browser's native Web Crypto API.
  • HMAC GeneratorGenerate HMAC-SHA1/256/384/512 signatures for API request signing. UTF-8/hex/base64 input.

Limitation: this note describes the DevTooKit tool and the general rule behind it. Production libraries, clocks, and schedulers can differ. Check those docs before you ship.