How to Validate a JWT
A JSON Web Token is trusted only after you verify it — yet the most common JWT bugs come from trusting the payload before (or instead of) checking the signature and claims. This guide walks through validating a JWT the right way: verify the signature, pin the algorithm to shut down alg:none and key-confusion attacks, and check expiry, issuer and audience before granting access.
In plain words
Validating a JWT means verifying its signature and then checking its claims before trusting it, and PlayCISO has a free, browser-based JWT Lab to see it in action. Pin the expected algorithm, reject alg:none, and verify exp, nbf, iss and aud — not just the signature.
The payload is only encoded, not encrypted, so never store secrets in it, and resolve keys by kid from a JWKS endpoint so the issuer can rotate signing keys without breaking live tokens.
Step by step
- 1
Always verify the signature first
A JWT is just base64url-encoded JSON until you verify its signature; the payload is readable and trivially editable by anyone. Verify the signature against your key before reading or trusting a single claim. If verification fails, reject the token outright — never fall back to using an unverified payload.
- 2
Reject alg:none and pin the expected algorithm
The infamous alg:none attack asks the server to accept an unsigned token. Explicitly reject it, and do not let the token header choose the algorithm. Configure your verifier with the exact algorithm (or small allow-list) you expect — for example only RS256 — so an attacker cannot switch algorithms to one you did not intend.
- 3
Prevent HS/RS key-confusion
If a system verifies RS256 tokens but also accepts HS256, an attacker can craft an HS256 token using your public RSA key as the HMAC secret, since the public key is not secret. Pinning the algorithm (previous step) closes this, and you should also keep symmetric and asymmetric keys in separate, type-specific verification paths so a verifier can never treat a public key as an HMAC secret.
- 4
Validate expiry and not-before
Check the exp claim and reject expired tokens; check nbf and reject tokens that are not yet valid. Allow only a small clock-skew tolerance (a minute or two) rather than a generous window. Expiry is what bounds the damage of a leaked token, so keep lifetimes short and do not skip this because the signature was valid.
- 5
Check issuer and audience
Verify that iss matches the issuer you trust and that aud contains your service identifier. The audience check stops a token minted for one service from being replayed against another that shares the same signing key. Treat a missing or mismatched iss or aud as a hard rejection, not a warning.
- 6
Resolve keys by kid via JWKS for rotation
Use the kid header to select the correct public key from the issuer JWKS endpoint, caching the key set and refreshing it when an unknown kid appears. This lets the issuer rotate signing keys without breaking live tokens. Validate that the kid maps to a key of the expected type and algorithm, and never fetch a key from a URL supplied inside the token itself.
- 7
Keep secrets and sensitive data out of the payload
The JWT payload is only encoded, not encrypted, so anyone holding the token can read every claim. Never put passwords, API keys or sensitive personal data in it. Store only the minimum claims needed for authorization, and if you genuinely need confidentiality use an encrypted token (JWE) rather than a plain signed JWT.
Decode a token, verify or break its signature, and see how alg:none and key-confusion attacks work — free, in your browser.
Related
Every PlayCISO cryptography how-to in one place.
Sign and verify with HMACThe HMAC behind HS256 JWTs, and how constant-time verification works.
Manage a key lifecycleHow JWKS key rotation fits into a full key lifecycle.
All free toolsThe full PlayCISO toolbox of scanners and reference tools.
Frequently asked questions
Is the content of a JWT encrypted?
No. A standard signed JWT (JWS) is only base64url-encoded, so anyone with the token can decode and read every claim in the payload. The signature protects integrity and authenticity, not confidentiality. If you need the contents to be secret, use an encrypted JWT (JWE) instead, and in all cases keep passwords, keys and sensitive personal data out of the claims.
What is the alg:none attack?
Some early JWT libraries honored a header of alg:none, which means the token is unsigned, and would accept it as valid. An attacker could then forge any payload with no signature at all. Modern libraries reject it, but you should still explicitly disallow none and, more importantly, pin the algorithm your verifier expects rather than trusting the value in the token header.
What is RS/HS key confusion and how do I prevent it?
If a verifier is told to accept both RS256 and HS256 and derives the key from the algorithm in the token, an attacker can sign an HS256 token using your RSA public key as the HMAC secret. Because the public key is not secret, the forged token verifies. Prevent it by pinning a single expected algorithm and keeping symmetric and asymmetric verification in separate paths so a public key is never used as an HMAC secret.
Which claims must I check beyond the signature?
At minimum check exp (reject expired tokens) and, if present, nbf (reject not-yet-valid tokens), allowing only a small clock-skew tolerance. Then verify iss matches your trusted issuer and aud contains your service, so a token for one audience cannot be replayed against another. Skipping these means a valid signature alone grants access, which is not enough.
How does key rotation work with JWTs?
The issuer publishes its public keys at a JWKS endpoint, each with a kid identifier, and signs tokens with a kid in the header. Your verifier looks up the matching key by kid, caching the key set and refreshing when it sees an unknown kid. This lets the issuer introduce a new key and retire an old one without invalidating tokens that are still in flight.
Can I just trust a JWT because it decoded successfully?
No. Decoding only reverses the base64url encoding; it proves nothing about authenticity. A token must have its signature verified against the correct key and its claims validated before you act on it. Treat an unverified decoded payload as attacker-controlled input, because that is exactly what it is.