JWT Decoder, Verifier & Signer
A JWT decoder that splits a token into its three segments, turns iat and exp into readable UTC times, and checks the signature against a secret or public key you paste.
| Claim | UTC time | Relative | Status |
|---|
No exp, iat, or nbf timestamp claims.
Paste the shared secret (HS256/384/512) or the public key in PEM or JWK (RS, PS, ES) to check the signature. It is verified on your device — nothing is uploaded.
Create & sign a token (HMAC)
Build a signed JWT from a payload and a shared secret. HMAC only — no private keys are handled.
How to use
- Put the token in the JWT to decode box. It comes apart as you type, and the Decode button beside it just re-runs the same thing.
- Read the Claims card first if you are chasing an expiry. The epoch numbers are already converted to UTC and exp carries the badge.
- For a signature check, paste the key into Verify signature: the shared secret for an HS token, or the issuer's public key as PEM or JWK for RS, PS and ES. Then press Verify signature and read the line underneath.
- Need a test token? Edit the Payload JSON in the card at the bottom, fill in Secret, choose HS256, HS384 or HS512, and press Generate token.
- Grab the result with the copy button to the right of the Signed JWT box.
About this tool
Auth is failing and the token is the first suspect. Drop it into the box at the top and it splits at the two dots into header, payload and signature, with the first two base64url-decoded and pretty-printed as JSON. Decoding fires on every keystroke, so the Decode button is only there for when you want to force it. Underneath, the Claims card pulls out iat, nbf and exp and gives each one a row with its UTC timestamp and a relative phrase beside it. Only exp gets a badge, and that badge reads valid or expired.
The Verify signature card takes whatever key the token needs. A raw shared secret for the HS algorithms. A PEM public key, the sort that opens with BEGIN PUBLIC KEY, for the RSA and elliptic-curve families. Or a JWK pasted as JSON, which it spots by the opening brace. Press the button and Web Crypto does the arithmetic, then the line below tells you the signature is valid or that it does not match, naming the algorithm either way. Eleven algorithms verify: HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256 and ES384. ES512 and EdDSA are not implemented, and you get a plain not-supported line for those rather than a wrong answer.
Below all that sits a small forge. Type a JSON object into Payload, put something in Secret, pick one of the three HS algorithms, and Generate token hands back a signed JWT with a copy button next to it. The header is written for you and always comes out as alg plus typ JWT, so there is no way to add a kid or any other custom header field. Signing stops at HMAC on purpose. I was not going to put a box on a public page that asks strangers for their RSA private key.
Keep two things straight while you read the output. A decode tells you what a token says about itself and nothing more, and a green verify line proves only that the signature matches the key you handed over, not that the key belongs to the issuer you have in mind. Your server still has to do the real authorisation. The expiry badge is a reading of a number in the payload, not a verdict on whether anyone will accept the token.
Signing the sample payload, then reading it back
The Payload box opens with a small object already in it: sub 1234567890, name Jane Doe, iat 1516239022. Leave it alone, type your-256-bit-secret into Secret, keep HS256 selected and press Generate token. You get eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cMErWtEf7DxCXJl8C9q0L7ttkm-Ex54UWHsOCMGbtUc, and it is that exact string every time, because HMAC is deterministic.
Copy it, paste it into the decode box at the top, and the Claims card shows a single iat row reading 2018-01-18 01:30:22 UTC with its age beside it. There is no exp in the payload, so no badge appears at all. Put your-256-bit-secret into the Verify signature box and press the button: "Signature is valid (HS256)". Change one character of that secret and the same press gives you "Signature does not match (HS256)".
Good to know
- A valid signature here means the token matches the key you pasted, and that is the whole of what it means. There is no JWKS fetch to find the issuer's real key for you, no check that the key belongs to whoever the iss claim names, and no policy evaluation of aud, iss or exp the way a server-side library would run one.
Frequently asked questions
Do I need the secret to read a token or see when it expires?
Neither one needs a key. The three segments are base64url encoded, which is transport, not encryption, so the header and payload open on their own and the Claims card converts exp the moment the token lands. A key matters only when you want to know whether the token was really signed by whoever it claims signed it.
Which algorithms can it actually verify?
Eleven. HS256, HS384 and HS512 against a shared secret, RS256, RS384 and RS512 plus PS256, PS384 and PS512 against an RSA public key, and ES256 and ES384 against an EC public key. ES512 and EdDSA are absent. Hand it one of those and it says so in the status line instead of quietly reporting a failed check.
Can I sign with an RSA private key here?
No, and that is a decision rather than an oversight. The signer offers HS256, HS384 and HS512 only, so the sole key it ever asks for is a shared secret. A private key pasted into a web page is a private key you have to treat as burned, and I would rather the field not exist at all.
Why won't my token decode when it looks fine to me?
Each segment is held to the base64url alphabet: letters, digits, hyphen and underscore, nothing else. A token that has been through a standard Base64 tool comes back carrying plus and slash characters and gets turned away. The other common cause is a missing segment, since it wants exactly three dot-separated parts and counts them before it tries anything.
The signature does not match here but the token works in production. What am I missing?
Usually the key is right and its shape is wrong. A trailing newline copied along with an HMAC secret changes the bytes being hashed. A secret stored base64-encoded has to be decoded before it is used, or you are signing with the wrong material. On the public-key side, check that you pasted an SPKI public key rather than a certificate or a private key, and that the alg in the header matches the kind of key you gave it.
Is it safe to paste a real access token in here?
The decode and the signature check both run with Web Crypto inside the page, and the token box is the one textarea on this site marked to skip the draft autosave, so what you paste there never reaches localStorage. A JWT is still a live credential, though. If a production token has been sitting on your clipboard, rotating it afterwards costs a minute and settles the question.
Missing something in JWT Decoder, Verifier & Signer? Suggest a feature →
Tell someone who needs this.