JWT Decoder
Decode JWT header and claims, warn about the algorithm and header tricks that get tokens forged, sign new tokens, and verify HS, RS, PS and ES signatures in your browser.
Related tools
- Encoder / DecoderEncode and decode Base64, URLs and HTML entities, and convert number bases.
- JSON ToolsFormat, minify, repair and validate JSON with exact error positions, browse it as a tree, and convert between JSON, CSV and YAML.
- Hash GeneratorHash text or a file with MD5, SHA-1, SHA-2, SHA-3 or HMAC.
About this tool
JWT Decoder splits a compact token into its three base64url parts, decodes the header and the payload as UTF-8 JSON (RFC 7519) and lists the time claims - exp, nbf, iat and auth_time - with their UTC values. The header is read for the tricks that get tokens accepted that should not be: alg “none”, an HMAC algorithm where the issuer signs with RSA or ECDSA (an attacker re-signs with the public key as the HMAC secret), jku, x5u, jwk and x5c pointing the verifier at a key the sender chose, crit parameters no verifier here implements, a missing kid, and an exp more than a year away. A jku or x5u URL is named, never fetched. It can verify the signature in the page: HS256, HS384 and HS512 against a shared secret, and RS, PS and ES at 256, 384 and 512 against a PEM public key, a JWK or a whole JWKS, whose key is picked by the token’s kid (RFC 7518). The same algorithms can sign: pick one, edit the header and payload, supply the secret or a PKCS#8 private key, and the token is built with Web Crypto in the page. Expiry is compared against your device clock, so the “expired” note is only as correct as that clock; the tool does not fetch a JWKS URL - the set has to be pasted - and does not check iss, aud or sub for you. A five-part JWE is refused rather than guessed at, because an encrypted token cannot be read without the decryption key.
The token, the HS secret and the public and private keys stay in the page: they are not sent to the XGM API and are never written into the URL, so a link you share carries the tool and nothing you pasted. Decoding uses atob and TextDecoder, and both signing and verification use Web Crypto - crypto.subtle.importKey for the raw HMAC secret, the PEM or the JWK, then crypto.subtle.sign or crypto.subtle.verify for HMAC, RSASSA-PKCS1-v1_5, RSA-PSS and ECDSA. A pasted JWKS is matched against the token's kid here in the page; no JWKS URL is fetched. The warnings are read out of the token's own header and claims in the same way: a jku or x5u header is named and never followed, and the secret you type is measured against the length RFC 7518 asks for without leaving the page. The JSON and Markdown exports are built and downloaded in the browser, nothing is written to local storage, and closing the tab removes everything you entered.
How to use it
- Open the JWT Decoder tool.
- Paste or type the input you want to inspect.
- Read the result, which is computed in your browser; the input is not sent to XGM.
- Copy the output only after checking it looks correct.
- Use related XGM tools if you need a broader diagnostic view.
FAQ
Which algorithms can this actually verify?
The HMAC family HS256, HS384 and HS512 with a shared secret, and the public-key families of RFC 7518: RS256/384/512 as RSASSA-PKCS1-v1_5, PS256/384/512 as RSA-PSS with a salt length equal to the hash length in bytes, and ES256, ES384 and ES512 as ECDSA over P-256, P-384 and P-521. Anything else, EdDSA included, is decoded but reported as unsupported for verification. A token whose header says alg “none” is flagged as critical and cannot be verified, because there is nothing to verify.
My HS256 secret is correct but the signature does not match. Why?
By default the secret is used as its raw UTF-8 bytes, exactly as typed. Many issuers store the secret base64 or base64url encoded, so the real key is the decoded bytes rather than the printable string - tick “Secret is base64url-encoded” in that case. Trailing spaces or a newline copied with the secret also change the bytes and therefore the HMAC (RFC 2104).
The tool says the token is expired but my API still accepts it.
exp and nbf are numeric seconds compared against the clock of the machine you are sitting at, not against the issuer's clock or a time server. Most server libraries also allow a leeway of some seconds around exp and nbf, so a token that just crossed the boundary can still pass there. If iat is more than five minutes in the future the tool raises a separate warning, which usually means the issuer's clock is wrong rather than the token.
Can I paste a JWKS, and why is my key rejected?
You can paste a single JWK or a whole JWKS object. When the JSON has a “keys” array, the key whose kid matches the token’s header is used; if no key carries that kid the tool says so and names the kids it found, rather than falling back to the first key and reporting a result you cannot trust. A token with no kid is matched only when the set holds one key, or one key of the right type for the algorithm; otherwise you are asked to paste the single key you mean. No JWKS URL is fetched - the set has to be pasted, because the issuer’s endpoint would refuse a cross-origin request from this page. In PEM form only an SPKI public key (-----BEGIN PUBLIC KEY-----) is accepted; certificates and private keys are not. The curve is taken from the header, so an ES256 header with a P-384 key fails at import rather than at verification.
What do the warnings above the claims mean?
They are the header and claim problems that get tokens forged, in severity order with alg “none” always first. Critical: alg “none” or a missing alg, an empty signature, and the jku, x5u, jwk and x5c headers, which tell a verifier to fetch or trust a key the sender picked - pin your keys and ignore those headers. Warning: an HS algorithm, because a verifier that reads the algorithm out of the token can be handed an RS256 token re-signed as HS256 with the public key as the secret; an HMAC secret shorter than the hash, which RFC 7518 §3.2 forbids; a missing kid on an asymmetric algorithm; crit parameters, which RFC 7515 says an unimplementing verifier must reject; no exp, or one more than a year out. There is no score - it is a list, and what does not apply is not shown.
Can I create a token here, and is the key safe?
Yes. Switch to “Encode and sign”, pick one of HS256/384/512, RS256/384/512, PS256/384/512 or ES256/384/512, edit the header and payload JSON, and supply the shared secret or a PKCS#8 private key (-----BEGIN PRIVATE KEY-----) or a private JWK. The token is built with crypto.subtle.sign in your browser: the key is never uploaded, never written into the URL and never stored. The header and payload are re-serialised compactly before signing, because a JWT carries the exact bytes that were signed, so the whitespace of the editor cannot be part of the signing input. Nothing is added for you - no iat, no exp - so the token holds exactly the claims you typed. A PKCS#1 or SEC1 key (-----BEGIN RSA PRIVATE KEY-----) is refused with the openssl command that converts it, because Web Crypto only imports PKCS#8.
Is the payload hidden from whoever holds the token?
No. A signed JWT is base64url encoded, not encrypted: anyone with the token reads the claims exactly as this page does, and the signature only proves the claims were not altered. The tool raises a warning when a claim name looks like a password, secret, card number or similar, because those values are effectively in the clear. Use a JWE, or keep the data server-side behind an opaque reference, when the content must stay private.