Blog · Guides

JWT validation: the steps, the traps and the libraries

The correct order for validating a JSON Web Token: algorithm allowlist, signature via JWKS, issuer and audience, expiry with clock skew, and the classic alg=none and HS256 confusion attacks.

Casdoor Team · November 1, 2026 · 3 min read

This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of November 1, 2026. Spotted a mistake? Tell us.

A JWT is three base64url parts: header, payload, signature. Decoding is not validating. Anyone can read the payload; only the signature check makes the claims trustworthy.

Most JWT vulnerabilities are not in the cryptography. They are in what the application chooses to check — and what it skips.

The validation order

  1. Parse the structure. Three parts, valid base64url, header is JSON. Reject anything malformed.
  2. Allowlist the algorithm. Read alg from the header, but decide what you accept from configuration. Reject none, reject algorithms you did not plan for.
  3. Get the key. Use kid to select a key from the issuer's JWKS, or a single configured key. Never let the token point to an arbitrary URL for its key.
  4. Verify the signature over the exact header and payload bytes, using the selected algorithm and key.
  5. Check iss against the expected issuer.
  6. Check aud against this service or application.
  7. Check exp and nbf with a small clock skew (a minute or two).
  8. Check iat if you care about how old the token is.
  9. Check application claims — tenant, role, scope — after the standard ones pass.
  10. Check replay if the token is single-use; otherwise rely on expiry.

Any step missing is a vulnerability, not a shortcut.

The classic attacks

alg: none. Some libraries historically accepted unsigned tokens. If your code trusts the header's algorithm, an attacker removes the signature and sets "alg":"none". Fix: allowlist algorithms in code.

Algorithm confusion. The server expects RS256 with a public key, but accepts HS256. The attacker signs a token with HMAC using the public key as the secret — and if the code uses the header's algorithm, verification succeeds. Fix: bind the algorithm to the key type.

kid injection. The token names the key to use. If the server resolves kid as a file path or URL, it can be pointed at attacker-controlled data. Fix: kid indexes a fixed key set.

Ignoring the audience. A token issued for the billing API is accepted by the admin API. Fix: check aud everywhere.

Trusting unverified claims first. Reading the user ID to decide which key to use, before verifying the signature. Fix: verify first, then read.

No expiry check. exp is just a claim until something enforces it.

JWKS and key rotation

Issuers rotate signing keys. A robust validator:

  • Fetches JWKS from the discovery document and caches it.
  • Refreshes when it sees an unknown kid, with rate limiting.
  • Accepts overlapping keys during rotation.
  • Fails closed if JWKS is unavailable — do not fall back to "accept".

Hard-coding a public key works until the first rotation, then every token fails at once.

Clock skew

Distributed systems disagree about time. Without a skew allowance, tokens fail a second before expiry or a second after issue. With too much skew, expired tokens stay valid.

A window of 60–120 seconds is typical. Handle nbf (not before) as well as exp.

Validate on every hop

In a microservice architecture, the edge does not validate on behalf of everyone. Each service that accepts a token should validate it, or the architecture should enforce mTLS and trust the edge.

The token's audience should also be service-specific if possible — a token for the orders service should not be accepted by the payments service.

Use a library, but configure it

Every mainstream language has maintained JWT libraries. Use one, and configure:

  • The accepted algorithms.
  • Expected issuer and audience.
  • Clock skew.
  • JWKS URL and refresh behavior.

Then add a test that feeds it a tampered token, an alg: none token, a wrong-audience token and an expired token — and asserts each is rejected.

How Casdoor issues tokens

Casdoor issues OIDC ID tokens and access tokens signed with its keys, and publishes the JWKS through the standard discovery document. Applications can validate tokens locally with any conforming library, or use the userinfo endpoint.

Related reading: ID token vs access token, what OpenID Connect is and where to store a JWT.