View all services
Talk to QA Advisor
Free tool · Automation and API

JWT decoder and verifier

Paste a JSON Web Token to see its header and claims, when it expires and whether its signature verifies. Then take the negative test tokens it generates and check that your API rejects every one. Nothing you paste leaves this browser.

JWT decoder

Paste a JWT to decode its header and claims, check its expiry and verify its signature. Everything runs in this browser; the token is never sent anywhere.

Verify the signature

Decode a token first.

How far exp and nbf may be off and still pass. Many libraries default to 0; match your API's setting.

Loading the example...

Ready
How to use

How to use the JWT decoder

  1. 1Paste the token, with or without the "Bearer " prefix. Line breaks and spaces from copying are ignored. The example is an HS256 access token signed a moment ago.
  2. 2Read the header, the payload and the claims table. Dates are shown in UTC, in your local time and relative to now, and the timing banner says whether the token is valid, expired or not yet valid.
  3. 3To verify the signature, enter the HMAC secret or paste the public key as PEM, JWK or JWKS. Use keys from a test environment, not production.
  4. 4Copy the negative test tokens and send each to your API in place of the original. Every one except the clock skew probe should be rejected with 401. Export the claims as Markdown for a defect report.

How a JWT is put together

A signed JWT is three base64url segments joined by dots: a header, a payload and a signature. The header is JSON naming the algorithm in alg and often a type in typ and a key ID in kid. The payload, which RFC 7519 calls the claims set, is a JSON object of claims such as sub, aud and exp. The signature is computed over the exact ASCII text of the first two segments and the dot between them, so changing a single character of either breaks it.

Base64url is the URL-safe form of base64: "-" and "_" replace "+" and "/", and the "=" padding is dropped (RFC 7515). Every header starts with "eyJ", which is simply the encoding of {", and that is a quick way to recognise a JWT in a log or a request.

An encrypted token, a JWE under RFC 7516, has five segments instead: a protected header, an encrypted key, an initialisation vector, the ciphertext and an authentication tag. Only its header can be read without the decryption key.

Decoding is not verifying

The header and payload of a signed JWT are encoded, not encrypted. Anyone holding the token can read them, exactly as this page does, without any key. That is why a JWT must never carry a password, an API key or personal data its holder should not see.

Decoding also proves nothing about who made the token. Anyone can write a header and payload and encode them. Only verifying the signature with the right key shows that the issuer created it and that nobody has changed it since. A frequent bug is code that calls a library's decode function, which skips verification, where it should call verify.

A full validation checks, in order: the algorithm is one the API expects, the signature verifies with a trusted key, the token has not expired and is already valid, the issuer is trusted, and the audience names this API. A test suite should prove each of those checks on its own.

The claims testers should check

RFC 7519 registers seven claims, all optional in the standard, which makes it the API's job to require the ones it relies on. In access tokens, exp, iss and aud should be mandatory, and sub identifies the user or client the API acts for.

  • exp and nbf

    Seconds since 1970 in UTC, as a JSON number. The token must be rejected on or after exp and before nbf. A string value such as "1767225600" is invalid, and some libraries skip the check rather than reject it.

  • aud

    A string or an array of strings. The API must reject a token whose audience does not include its own identifier, or a token issued for one service can be replayed against another that trusts the same issuer.

  • iss

    Compared exactly with the issuers the API trusts. When an API trusts several issuers, iss also decides which key should have signed the token.

  • scope, scp and roles

    Authorisation, not authentication. A valid token with too little scope should get 403 Forbidden, not 401, and not the data.

  • typ

    RFC 8725 recommends explicit types, such as at+jwt for access tokens (RFC 9068), so an ID token cannot be passed off as an access token.

Validation bugs to test for

RFC 8725, the JWT best current practice, lists the attacks that have broken real systems. Each maps to a negative test you can run against your own API, and the tool above generates the tokens for most of them.

  • alg "none"

    An unsecured token with an empty signature. A library that honours whatever alg the token names will accept it. Test "none" and mixed-case variants such as "nOnE".

  • Algorithm confusion

    An RS256 token rewritten as HS256 and signed with the server's public key as the HMAC secret. If the API lets the token choose the algorithm, it verifies. The API should fix the algorithm per key.

  • Signature not checked

    Change one claim and keep the original signature. If the request still succeeds, the API is decoding rather than verifying.

  • Weak HMAC secrets

    RFC 7518 requires an HS256 key of at least 256 bits. A short or well-known secret can be found offline from a single token, after which anyone can mint tokens.

  • kid, jku and x5u

    Header values are attacker controlled. kid can carry path traversal or SQL injection into a key lookup, and following jku or x5u lets the sender pick the verification key and opens server-side request forgery.

  • Wrong audience or issuer

    A token that is genuine but meant for another service or issued by another tenant must still be rejected.

Testing token expiry and clock skew

Servers' clocks drift, so RFC 7519 allows a small leeway, usually no more than a few minutes, when checking exp and nbf. That leeway is a boundary worth testing. With a 60-second allowance, a token that expired 30 seconds ago should be accepted and one that expired 90 seconds ago rejected. Find the configured value, then test just inside and just outside it.

Test the moment of expiry itself: the token must be rejected at exp, not one second after. Check a token with nbf in the future, a token with no exp at all, and one whose exp is a string. And check what the client does when its token expires mid-session: it should refresh once and retry, not loop or log the user out.

To test these checks independently of the signature check, the expired or changed token must carry a valid signature. Enter a test environment secret or private key in the tool above and the negative test tokens are re-signed in your browser.

Keeping tokens private

A JWT is a bearer credential: whoever holds it can use it until it expires. Pasting a live production token into a website that logs requests gives that site your session. This page decodes, verifies and signs entirely in your browser with Web Crypto and sends nothing to any server, but the safer habit is still to use tokens from test environments.

In defect reports, include the decoded header and claims rather than the token. The Markdown export leaves the signature out for that reason. Logs deserve the same care: an application that writes full Authorization headers to its logs turns every log reader into a potential user of every account.

QAble service

Security testing

QAble tests authentication and token handling the way attackers do: forged and expired tokens, algorithm confusion, broken audience checks and access control, on every release.

Explore the service
FAQ

Frequently asked questions

Is it safe to paste a JWT into this decoder?

The token never leaves your browser: decoding, verification and signing all run locally with Web Crypto, and nothing is sent to a server. Even so, a token is a credential, so prefer tokens from a test environment and never paste a production signing key anywhere.

Can I decode a JWT without the secret?

Yes. The header and payload of a signed JWT are only base64url encoded, so anyone can read them without a key. The secret or public key is needed only to verify the signature, which is what proves the token is genuine and unchanged.

Why is my token not decoding?

The usual causes are a token cut short when copying, which leaves too few segments or an invalid length, quotes or other text copied with it, or a value that is not a JWT at all, such as an opaque access token. The error message says which segment failed and why. A token with five segments is encrypted and cannot be decoded without the key.

What is the difference between JWS and JWE?

A JWS (RFC 7515) is signed: three segments, with a readable payload protected against changes. A JWE (RFC 7516) is encrypted: five segments, with a payload only the holder of the decryption key can read. Most access and ID tokens are JWS.

Why does the signature not match?

Either the token was changed after signing or the key is not the one that signed it. Check you have the right environment's key, that a base64 secret is set to Base64 rather than Text, and for a key set that the kid matches. An ES256 signature must be 64 bytes of raw r and s; a DER-encoded signature will not verify.

Which algorithms can it verify?

HS256, HS384 and HS512 with a secret; RS256, RS384, RS512, PS256, PS384 and PS512 with an RSA public key; ES256, ES384 and ES512 with an EC public key; and EdDSA with Ed25519 where the browser's Web Crypto supports it, which current Chrome, Edge, Firefox and Safari do.

Need the edge cases found for you?

QAble testers work through the inputs, payment paths and data that break products in production, before your users find them.

Talk to QA Advisor