JWT Verify
Verify a JWT signature and check its claims. Reports signature and claim validity separately. Supports HS, RS, PS, ES, and EdDSA.
Verify a JWT signature against an HMAC secret, PEM public key, or JWK. Reports signature and claim validity separately. Pins the algorithm. Runs in your browser. Read more Show less
What is JWT verification?
A JSON Web Token is a signed statement. The signature is what makes the token trustworthy: it proves that whoever issued the token held the private key (or shared secret) named in the header, and that the payload has not been modified since. Decoding a JWT does not prove anything - anyone can produce a token with any payload and any header.
Verification has two independent parts. The first is the signature check: recompute the signature over the header and payload using the key, and compare. The second is the claim check: read the payload and decide whether the claims are acceptable right now - is the token expired, is it active, does the issuer match, is the audience correct. A token can pass one and fail the other.
How to use
Paste the compact JWT into the left box. Paste the verification key into the key box on the right and pick the key type: HMAC secret, PEM public key, or JWK public key. The result appears in the output box.
Set the "Expected algorithm" dropdown to the algorithm you expect. If the header declares a different one, the tool refuses to verify - this is the recommended way to use it. Leaving it on "Auto" reads the algorithm from the token header, which is convenient but does not defend against a forged algorithm value.
For HMAC tokens (HS256, HS384, HS512), the key is the shared secret - the same value that was used to sign. For RSA, RSA-PSS, ECDSA, and EdDSA, the key is the public key. The tool will not accept a private key in the key field, by design.
Signature and claims are different checks
The signature check answers a cryptographic question: was this token issued by someone holding the key. The claim check answers a policy question: is this token acceptable right now. The two failures look different and mean different things.
A valid signature with an expired exp claim means the token is authentic but no longer usable. An invalid signature with a future exp claim means the token claims to be usable but cannot be trusted. This tool reports both verdicts separately so the distinction is clear.
Why the algorithm matters
JWT headers carry the algorithm name, and a naive verifier trusts it. That leads to the classic algorithm confusion attack: an attacker takes an RS256 token, changes the header to HS256, and signs the new token with the RSA public key as if it were an HMAC secret. A verifier that reads the algorithm from the header and uses whatever key it was given will accept the forgery.
The defense is to pin the algorithm. If the verifier is told "this must be RS256", an HS256 token is rejected before any cryptography is attempted. This tool pins the algorithm to the value in the "Expected algorithm" dropdown. On "Auto" it uses the header value and prints a note recommending that you set it explicitly.
The tool also refuses tokens with "alg": "none". An unsecured JWT has no signature to check, so it cannot be reported as valid.
FAQ
Why does my valid signature show as invalid?
Three common causes. The key is wrong for the token - a secret from a different environment, or a public key that does not match the private key that signed. The algorithm is wrong - an RS256 token verified against an HS256 secret, or vice versa. Or the token text was truncated in copy-paste; compact JWTs are long and a single missing character invalidates the signature.
Can I verify tokens signed by Auth0, Okta, or Cognito?
Yes, if you have their public key. Fetch the JWKS from the provider, copy the matching JWK for the token's kid, and paste it into the key box with "JWK public key" selected. The tool verifies the signature and the standard time claims. It does not fetch JWKS from a URL - paste the key.
What does the "Auto" algorithm option do?
It reads the alg value from the token's header and uses that to verify. This is convenient but does not defend against algorithm-confusion attacks. Set the dropdown to a specific algorithm for any security-relevant check.
Why will the tool not accept a private key?
Verification needs the public key only. Accepting a private key would encourage the habit of pasting secrets into online tools, which is the wrong habit. If you have a private key and need the public one, derive it locally first with openssl rsa -in key.pem -pubout or the equivalent.
Does the tool check issuer, audience, or other claims?
No. It decodes the payload and shows the standard time claims (exp, nbf, iat) with their current status. Policy claims like iss, aud, and sub are shown but not evaluated - those depend on your application's expectations.
Is my token sent anywhere?
No. The token and the key stay in your browser. The verification is done by the jose library running in the same tab. You can confirm this in the browser's Network tab.
Command line equivalent
# Node (jose)
npm i jose
node -e 'const {compactVerify,importSPKI}=require("jose");(async()=>{const t=process.argv[1];const k=new TextEncoder().encode("your-256-bit-secret");const r=await compactVerify(t,k,{algorithms:["HS256"]});console.log("valid",new TextDecoder().decode(r.payload));})()' "$TOKEN"
# Python (PyJWT)
pip install pyjwt
python3 -c "import jwt,sys; print(jwt.decode(sys.argv[1], 'your-256-bit-secret', algorithms=['HS256']))" "$TOKEN"
# jwt-cli (Rust)
cargo install jwt-cli
jwt decode --secret your-256-bit-secret "$TOKEN"
# openssl (RS256 with a PEM public key, manual)
# Extract the signing input, decode the signature, and verify with openssl dgst
# -verify pubkey.pem -signature <(base64url-decode signature) <(echo -n header.payload)