JWT Validation Mistakes AI Tools Generate
Validate JWT signatures, algorithms, issuers, audiences, time claims, and token types with a deployment-specific trust policy.

On this page
- The vulnerable pattern: decode and trust
- Why generated validation is incomplete
- Define the trust policy before code
- Validate the cryptographic result and the claims
- Key selection and rotation
- Safe local verification tests
- Token storage, replay, and session state
- What the local inspector can and cannot prove
- Sources
Direct answer: Accept a JWT only through a deployment-specific validation policy. Verify its signature with trusted key material, allow only expected algorithms, require the exact issuer and audience, enforce time claims, and apply rules for the intended token type. Decoding the header and payload is useful inspection, but it proves neither integrity nor session validity.
A JWT is a compact container. Its claims may describe a user, audience, issuer, and expiry, but those strings are not trustworthy until the consumer verifies the token and decides it fits the expected use. A correctly signed token can still be wrong for your API.
The vibe coding security guide treats token validation as one authentication boundary. Authorization still decides whether the validated principal may perform the requested action.
The vulnerable pattern: decode and trust
Vulnerable pattern. Use a fake token only.
app.use((req, res, next) => {
const token = readBearerToken(req)
const claims = decodeJwt(token)
req.user = {
id: claims.sub,
role: claims.role,
}
next()
})
Decoding Base64URL segments does not verify a signature. The caller controls the header and payload of an unverified token. Even when a library verifies some signature, a loose configuration may accept an unintended algorithm, issuer, audience, or token type.
This bug hides behind friendly API names. decode() and parse() sound close to validation, and generated code often stops once it can read sub.
Why generated validation is incomplete
An AI tool can see the token structure but not the deployment trust relationship. It may not know which identity provider is authoritative, whether the API accepts access tokens or ID tokens, which client or service is the audience, or how key rotation works.
Library defaults also vary. Some calls require an algorithm but do not require an issuer. Others validate exp but leave aud optional. Generated code may copy a short example that omits production configuration for clarity.
The token header can influence key and algorithm selection. RFC 8725 says callers should allow only algorithms chosen by the application and ensure each key is used with the intended algorithm. Do not let an arbitrary alg value select a different verification behavior.
Cross-JWT confusion is another problem. An ID token can be valid for a client application but unsuitable as an API access token. A password-reset token and an ordinary session token need different acceptance rules even if they share a signing service.
Define the trust policy before code
Write down the expected values and sources:
- Token purpose, such as API access
- Exact issuer identifier
- Accepted audience or audiences
- Allowed signing algorithm or algorithms
- Trusted key set location or pinned key
- Required claims and clock-skew policy
- Token type and any provider-specific claim
- Revocation or server-session check, if the product supports one
Avoid accepting several unrelated issuers through one permissive callback. Each trust relationship should have its own validation profile.
A corrected pattern passes the policy to a maintained verification library.
const claims = await verifyJwt(token, {
issuer: "https://identity.example/",
audience: "urn:test-api",
algorithms: ["RS256"],
keySet: trustedKeySet,
requiredClaims: ["sub", "exp", "iat"],
})
if (claims.token_use !== "access") {
throw new InvalidTokenError()
}
The example uses a reserved test origin and illustrative library shape. Follow the API of the maintained library you actually deploy. Do not implement signature primitives by hand.
Validate the cryptographic result and the claims
Signature verification proves that the token bytes match a trusted signing key. It does not prove that the signer is the issuer your API expects unless key trust and issuer configuration agree.
Require the exact issuer. Avoid prefix or substring matching. Validate the audience according to the token profile. An OpenID Connect ID token has audience rules tied to the client. An API access token may use a different audience defined by the authorization server.
Check exp and reject expired tokens. Apply nbf when present, using a small documented clock tolerance rather than an unlimited grace period. iat can help detect surprising issuance times but is not an expiry by itself.
Do not use a missing exp as a reason to create an endless application session unless the token profile explicitly allows that design. Long-lived tokens increase the window for replay and make revocation more important.
Validate the subject before using it as a database identifier. A valid sub is scoped to its issuer. The same text from a different issuer is not automatically the same user.
Key selection and rotation
For asymmetric signing, providers often publish a JSON Web Key Set. Fetch it only from the configured issuer relationship. Cache keys with bounded lifetimes and handle planned rotation without accepting keys from arbitrary URLs supplied in token headers.
Treat kid as a selector within the trusted set, not a path, query, or remote location. A missing key should lead to a controlled refresh and then denial, not a fallback to skipping verification.
Bound key-set retrieval like any other network dependency. Use the issuer’s configured HTTPS location, response-size and time limits, and a cache policy that can survive a short provider outage without trusting stale material forever. An attacker-controlled jku, x5u, or similar header must not redirect the verifier to an arbitrary key server. If the product supports such headers, pin them to the established trust relationship and library guidance.
For symmetric algorithms, the verifier and issuer share a secret. Never use a public asymmetric key as an HMAC secret. Keep different token families on separate keys where practical so a token accepted in one context cannot cross into another.
Safe local verification tests
Use locally generated test keys or an identity provider’s dedicated test tenant. Never alter a real user’s token or test an unapproved production API.
Create known cases:
- A valid access token for the exact issuer and audience succeeds.
- A token with changed payload bytes fails signature verification.
- A valid token for another audience fails.
- A valid token from another issuer fails.
- Expired and not-yet-valid tokens fail outside the documented skew.
- An ID token fails at an access-token endpoint.
- An unapproved algorithm and an unsecured token fail.
- A rotated trusted key succeeds only after the controlled key-set refresh.
Test error handling too. Return a generic authentication failure. Do not echo the token, claims, signing material, or detailed library exception into logs that broad audiences can read.
Token storage, replay, and session state
JWT validation does not solve token theft. A bearer token can be replayed by whoever possesses it until it expires or the server rejects it through another mechanism. Use TLS, short lifetimes suited to the risk, careful logs, and a deliberate browser storage design.
Server-side session state can support revocation, logout, device policy, or account disablement. A valid signature alone may not reflect those current decisions. Document whether the API checks a session record, token version, or revocation signal.
Refresh tokens need their own profile and storage rules. Do not accept one at a normal API endpoint merely because its signature is valid. Rotate or revoke them according to the provider contract, and test that an access-token validator rejects the refresh-token type.
The related client-side auth guide explains why a browser role or decoded claim cannot be the decisive access-control layer.
What the local inspector can and cannot prove
The browser-local JWT and session inspector decodes a token you paste and points out claim signals. It does not upload the token.
It does not possess your issuer’s trusted keys, validate the signature, confirm audience policy, check current revocation, or prove that a server accepts or rejects the token. Treat its output as local inspection. Never paste a live production token into any tool unless the handling policy explicitly permits it.
Automated tests can exercise the configured profiles. They cannot prove that every deployed endpoint uses the right profile or that authorization after authentication is correct.
Sources
Frequently asked
Does decoding a JWT verify it?
No. Base64URL decoding only reveals the header and claims. Acceptance requires cryptographic verification plus issuer, audience, time, and token-type checks, along with any session or revocation check the application's policy actually uses.
Should an API accept alg none?
A signed-token consumer should reject unsecured tokens and allow only the algorithms configured for that trust relationship. The token header must not choose an unapproved algorithm.
Why validate both issuer and audience?
Issuer identifies the authority that minted the token. Audience identifies the intended recipient. Checking both prevents a valid token from another trust domain or service from being accepted here.
Where should a browser store a JWT?
Storage depends on the application threat model. Script-readable storage increases exposure to XSS, while cookies introduce CSRF considerations. Server validation is required regardless of storage.
Related posts
- Keep AI Agents Away From Production Deletes
Block standing production delete authority with separate identities, permission ceilings, exact approvals, short-lived access, and audit records.
- AI App Pre-Launch Security Checklist
Run an evidence-based pre-launch review across authorization, secrets, inputs, dependencies, deployment, agents, monitoring, recovery, and retests.
- Why AI-Generated Tests Are Not Security Proof
Use AI-generated tests as regression checks, then add independent invariants, negative cases, mutation checks, and scoped retest evidence.