Verify the signature of a JSON Web Token (JWT)

verifies the signature of a JWT (HS256, HS384, HS512, RS256, RS384, RS512) using a secret or a public key, and inspects its claims
For HS256 / HS384 / HS512: the shared secret string. For RS256 / RS384 / RS512: the public key in PEM format.

Why verify a JWT's signature?

A JSON Web Token (JWT) breaks down into three parts separated by dots: header.payload.signature. Decoding a JWT just means reading the first two parts (which are Base64URL). Anyone can do it, and anyone can build a JWT with the payload of their choice. What makes a JWT trustworthy is only the signature: without verification, accepting a JWT is like letting in anyone who claims to be someone, without asking for ID.

This tool verifies the signature of a JWT against a key. It does not just decode: it recomputes the signature from the header, the payload and your key, then compares it with the token's signature. If the two match, the token is authentic. Otherwise, it was forged, modified, or signed with another key.

Verification is the cornerstone of any architecture that uses JWTs for authentication or authorisation: without a valid signature, the payload can lie. An attacker who modified "role":"user" to "role":"admin" would have no trouble doing so if the server only checked the token's format and not its signature.

Common algorithms

The JWT specification (RFC 7518, JSON Web Algorithms) defines several algorithm families. Here are the most-used in production:

  • HMAC (HS256, HS384, HS512): symmetric signature based on HMAC-SHA. The same secret key is used to sign and to verify. Simple to set up, performant, but any party able to verify the token is also able to issue one. Suited to scenarios where the issuer and the verifier are the same team or the same service.
  • RSA (RS256, RS384, RS512): asymmetric signature. The private key signs, the public key verifies. Ideal when the issuer and the verifiers are distinct entities (OAuth2, OpenID Connect, identity federation). It is the preferred algorithm of most public identity providers.
  • ECDSA (ES256, ES384): asymmetric signature on elliptic curves. Same logic as RSA (private key to sign, public key to verify) but with more compact keys and signatures for an equivalent security level. Increasingly widespread in modern architectures.

How to provide the key

The expected key format depends on the algorithm declared in the JWT header:

  • HS256, HS384, HS512: the key is a secret string. It is the secret shared with the issuer, often stored in an environment variable like JWT_SECRET. No special formatting, just the raw value.
  • RS256, RS384, RS512: the key is an RSA public key in PEM format, starting with -----BEGIN PUBLIC KEY----- and ending with -----END PUBLIC KEY-----. Keep the line breaks as is, otherwise OpenSSL refuses to parse it.
  • ES256, ES384: the key is an ECDSA public key in PEM format, on the matching curve (P-256 for ES256, P-384 for ES384).

Example of an expected RSA public key

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxe...
...vQIDAQAB
-----END PUBLIC KEY-----

How does verification work?

Our server reproduces exactly the operation the issuer performed:

  1. It splits the JWT into header, payload and signature.
  2. It concatenates base64url(header) + "." + base64url(payload).
  3. For HMAC, it computes an HMAC-SHA-256/384/512 with your secret key, then compares with the received signature using hash_equals (constant-time comparison to avoid timing attacks).
  4. For RSA, it calls openssl_verify with your public key in PEM format.

Use cases

  • API authentication debugging: you get a 401, check whether your token is indeed signed with the expected key.
  • Validating a token received from a provider: a partner (Auth0, Keycloak, Cognito, Okta) sends you JWTs signed with RS256; you want to confirm they really come from them using their public key.
  • Security audit: check that a third-party service correctly signs its tokens with a robust algorithm, and not in HS256 with a weak secret.
  • Manual tests: check that a JWT generated by your code passes verification with the provided public key.
  • Quick verification of a received token: during integration of an SSO or a partner API, check in seconds that the signature/key chain works before writing a single line of code.

How to use the tool

  1. Paste the full JWT (the three parts separated by dots).
  2. Provide the key matching the algorithm in the header:
    • For HS256, HS384 or HS512, the key is the shared secret string with the issuer. It is a free string, often stored in an environment variable like JWT_SECRET.
    • For RS256, RS384 or RS512, the key is the public key in PEM format, starting with -----BEGIN PUBLIC KEY----- and ending with -----END PUBLIC KEY-----.
  3. Launch verification. The tool displays the status (valid or invalid) and the decoded payload.

Common pitfalls to avoid

  • Algorithm "none": the spec allows alg: none, meaning "no signature". A classic flaw is to build a token with this header hoping that the server will accept it. Our tool systematically rejects tokens with alg: none.
  • HMAC vs RSA confusion (algorithm confusion): an attacker changes the RS256 algorithm to HS256 and signs the payload with the RSA public key used as an HMAC secret. If the server does not enforce the algorithm, it accepts the token. Always lock the expected algorithm server-side.
  • HMAC secrets hard-coded: a secret committed to a Git repository makes the trust of all tokens void. Store secrets in environment variables or an application vault.
  • Public key vs private key: to verify an RSA or ECDSA-signed JWT, provide the public key, never the private one. The private one only serves to sign and must never leave the issuer.
  • Ignored expiry: a valid signature on an expired token must never be accepted. Remember to check exp and nbf.
  • Unchecked audience: a token intended for API A should not be accepted by API B. Check the aud claim.

Temporal claims: exp and nbf

Beyond the signature, a valid JWT must also respect its temporal constraints:

  • exp (expiration): the token is no longer valid after this date.
  • nbf (not before): the token is not yet valid before this date.

Our tool explicitly flags when a token is expired or not yet valid, even if its signature is correct. This matters: a valid signature on an expired token must never be accepted in production.

Difference with our JWT decoder

Our JWT decoder just decodes the header and payload parts to make them readable. It performs no verification of the signature and does not ask for a key. Use it to quickly inspect a token's content. Use the JWT verifier (this page) as soon as you need to prove that a token is authentic. To build a signed JWT for testing purposes, use our JWT builder.

Frequently asked questions

Why RS256 rather than HS256?

With HS256, issuer and verifier share the same secret: any verifier can therefore issue tokens. It is manageable when you control both ends. As soon as you talk about a single identity provider with several consuming services, switch to RS256: the issuer keeps the private key, the public key is distributed to every API that must verify. No consuming API can then forge tokens.

How do I retrieve the public key of an identity provider (IdP)?

Most IdPs expose a standardised JWKS endpoint (for example https://example.com/.well-known/jwks.json). This endpoint returns a JSON containing the active public keys. You can convert the JWK entry matching the kid in your JWT header to a PEM key via the openssl command or via a JWKS library in your stack (for example jose-jwt, jwks-rsa).

What if verification fails?

First, check the algorithm: a token signed in HS256 cannot be verified with an RSA key, and vice versa. Then check the key: a stray whitespace, a missing line break in a PEM key, or an HMAC secret slightly different from the one used by the issuer are enough to make verification fail. If the IdP rotated its key, your kid may point to a key you no longer have.

What is JWKS?

JWKS (JSON Web Key Set, RFC 7517) is a JSON format that describes a set of public keys. Each key is identified by a kid (key ID) and the JWT to verify references this kid in its header. The mechanism allows the IdP to rotate its keys without breaking verifiers: they simply query the JWKS endpoint to retrieve the key matching the kid of the received token.

How do I generate an RSA key pair to sign my JWTs?

With OpenSSL: openssl genrsa -out private.pem 2048 then openssl rsa -in private.pem -pubout -out public.pem. The private key signs on the issuer side, the public key verifies on the consumer side. For new services, prefer 3072 or 4096 bits.

Should the JWT be encrypted in addition to being signed (JWE)?

A signed JWT (JWS) guarantees integrity and authenticity, but the payload remains readable by anyone who gets it. If the token contains sensitive data (internal identifiers, detailed rights, personal data), consider the JWE format (JSON Web Encryption) which encrypts the payload on top of signing it.

Example request

curl -X POST https://cdrn.fr/api/v1/tools/jwt-verifier/execute \
  -H "Content-Type: application/json" \
  -d '{"token":"...","key":"..."}'

Input schema

Field Type Required Default
token text
key text

Endpoints

  • GET https://cdrn.fr/api/v1/tools - lists every available tool
  • GET https://cdrn.fr/api/v1/tools/jwt-verifier - returns the schema for this tool
  • POST https://cdrn.fr/api/v1/tools/jwt-verifier/execute - runs this tool with a JSON payload