How to validate the JWT iss claim and why it matters
Why skipping issuer validation is the most common JWT security mistake, and how to fix it.
If you work with JWTs in any capacity, you've seen the iss claim sitting near the top of every token payload. It's one of those fields that feels self-explanatory, it just says who issued the token, right? But in practice, the iss claim is one of the most frequently under-validated claims in production systems, and getting it wrong can let an attacker mint tokens that your API happily accepts.
This article covers what the iss claim is actually for, the specific ways developers mishandle it, and the concrete validation patterns you should implement to keep your system secure.
!!Need to inspect a token? Use the WorkOS JWT Debugger to decode and inspect your JWTs directly in the browser. It's a quick way to verify your token's iss, aud, sub, and other claims while debugging.!!
What is iss in JWT?
The iss (issuer) claim identifies the principal that issued the JWT. As defined in RFC 7519, Section 4.1.1, its value is a case-sensitive string (typically a URL) that uniquely identifies the authorization server that created and signed the token. The iss claim is a mandatory claim, and must be included in every JWT.
A typical JWT payload looks like this:
The iss claim here tells the receiving service: "This token was created by https://https://api.workos.com." The receiving service should verify this claim before trusting anything else in the token.
If the issuer isn't one you explicitly trust, the token should be rejected.
This sounds straightforward. In practice, it's where a surprising number of security bugs originate.
Why the iss claim matters
Think of JWT validation like checking a passport:
exp→ Is it expired?aud→ Is it meant for me?iss→ Did a trusted authority issue it?
If you skip issuer validation, you’re basically saying: “I’ll accept a passport… from anyone who prints one.”
That’s a fast lane to security issues.
How to validate the iss claim
When your backend receives a JWT, it should:
- Extract the
issvalue. - Compare it against a trusted list of issuers.
- Reject the token if it doesn’t match.
This ensures you only accept tokens from systems you explicitly trust.
iss + signature validation: A duo, not a substitute
A common misconception is that if the signature is valid, you don't need to check iss. This is not true.
The signature is used to verify that the sender of the JWT is who they say they are and to ensure that the message wasn't tampered with along the way.
Without checking iss, a malicious actor could:
- Generate a valid token.
- Sign it with their own key.
- Claim to be your trusted provider.
Unless you verify the issuer, your system might accept it.
Common mistakes with the iss claim
1. Not validating iss at all
The most dangerous mistake is also the most common: simply not checking the issuer. Many developers validate the signature and the exp claim and assume that's sufficient. It isn't.
Without issuer validation, your API will accept a validly signed token from any issuer, including an attacker's own authorization server. If your signature validation uses a JWKS endpoint derived from the token itself (a pattern some libraries encourage), the attacker controls both the key and the token. Your API sees a valid signature and lets them in.
Here's the vulnerable pattern:
And here's what it should look like:
2. Accepting any issuer from a dynamic list without proper controls
In multi-tenant systems, you often need to accept tokens from multiple issuers. The mistake here is building a list of trusted issuers that grows dynamically without strict controls; for example, pulling the list from a database that a tenant admin can modify, or auto-trusting any issuer that appears in a federation metadata document.
If an attacker can add their own issuer URL to your trusted list, they can issue tokens your system will accept. The trusted issuer list should be treated as a security-critical configuration, not as application data.
3. String matching pitfalls
The iss claim is a case-sensitive string. Subtle differences between https://auth.example.com and https://auth.example.com/ (trailing slash) will cause validation to fail, or worse, cause you to implement a loose comparison that an attacker can exploit.
Other common string issues include scheme mismatches (http vs https), port numbers (:443 vs omitted), and path casing. These are especially problematic when your auth provider changes their issuer URL format in an update and your hardcoded string stops matching.
4. Using iss to derive the JWKS endpoint without a whitelist
Some libraries and frameworks fetch the signing keys from a URL derived from the iss claim. For example, by appending /.well-known/openid-configuration to the issuer URL. This is standard OpenID Connect behavior, but it becomes a vulnerability if you don't first verify that the issuer is one you trust.
Without a whitelist check, an attacker sets iss to their own server, your library fetches the attacker's JWKS, and the signature validates successfully against the attacker's keys. The token is forged, but your system thinks it's legitimate.
How iss interacts with other claims
The iss claim doesn't exist in isolation. It interacts with several other claims, and understanding these relationships is important for correct validation.
- iss + sub: The subject (
sub) claim is only unique within the scope of an issuer. User12345from Okta and user12345from Azure AD are completely different people. Your user lookup should always key on the combination ofissandsub, neversubalone. Getting this wrong is a real identity confusion vulnerability in federated systems. - iss + aud: While
isstells you who made the token,audtells you who it's for. Both must be validated. A validly issued token from a trusted issuer is still invalid if it wasn't intended for your service. Token forwarding attacks exploit exactly this gap: a token issued for Service A gets forwarded to Service B, which accepts it because the issuer is trusted. - iss + kid: The
kid(key ID) in the JWT header tells you which specific key from the issuer's JWKS was used to sign the token. The validation flow should be: checkissagainst whitelist → fetch JWKS for that issuer → find the key matchingkid→ verify signature. Never reverse this order.
Best practices for iss claim validation
- Always validate the issuer. Never skip
issvalidation, even in development or internal services. Use your JWT library's built-in issuer check, don't roll your own string comparison. - Maintain an explicit whitelist. Every trusted issuer should be explicitly configured. No auto-discovery, no dynamic registration without human approval.
- Bind JWKS endpoints to specific issuers. Never derive the JWKS URL from the token's
issclaim without first checking the issuer against your whitelist. - Use exact string matching. The
issclaim is case-sensitive. Normalize trailing slashes and schemes in your configuration, but match exactly. - Combine iss + sub for user identity. In any system that accepts tokens from multiple issuers, always use the issuer/subject pair as the user's unique identifier.
- Log and alert on untrusted issuers. A token with an unrecognized issuer hitting your API is a signal worth investigating. Don't just reject silently, log it.
Final thoughts
The iss claim is deceptively simple. It's just a string that says who made the token. But that simplicity hides real security weight: it's the foundation of your trust chain.
The fix isn't complicated. Validate the issuer. Maintain a whitelist. These are small engineering decisions that prevent entire categories of token forgery attacks.