All Articles

JWT Signature Bypass via SSRF in iss Claim

How attackers exploit unverified iss claims in OIDC/JWT implementations to forge tokens, bypass signature verification, and gain unauthorized access — with real code examples and fixes.

Modern web and mobile apps rely on OAuth 2.0 with OpenID Connect (OIDC) to authenticate users and authorize API calls. OIDC adds an identity layer to OAuth so that an Identity Provider (IdP) — such as Amazon Cognito, Okta, or Auth0 — can issue JSON Web Tokens (JWTs) that your backend can validate locally.

Each JWT is a compact, signed set of claims (who the user is, what they can access, when the token expires). To verify a JWT, your API needs the IdP’s public keys, which are typically published as a JWKS (JSON Web Key Set). These are discovered from the issuer (the iss claim) via the OIDC discovery document at:

https://<issuer>/.well-known/openid-configuration

which points to the JWKS endpoint (commonly .../.well-known/jwks.json).

In the past, security researchers have found issues like remote key injection via jku and OIDC discovery–driven SSRF. Along the same lines, we have found another interesting attack vector that leads to a JWT signature verification bypass allowing unauthorized access.


Key Concepts

OIDC — A standard layer on top of OAuth that defines how IdPs authenticate users and expose metadata (including where their JWKS lives).

JWT — A signed envelope of claims such as iss (issuer), sub (subject/user), aud (audience/your API), and exp (expiry).

JWKS — A JSON document containing the issuer’s public keys — your server uses these to verify JWT signatures.


The Vulnerability

Signature bypass occurs when an application dynamically pulls the public keys (JWKS) from the issuer and caches them, but implicitly trusts the endpoint received in the iss claim. In this implementation, a common (and insecure) approach is to:

  1. Read iss from the token
  2. Make a network call to download the JWKS public keys
  3. Verify the token’s signature with those keys

Many quick-start guides point to exactly this pattern.

When an application follows this implementation, it leads to signature bypass: an attacker can forge a JWT signed with their own private key and set the iss claim to an attacker-controlled domain that serves a compliant JWKS. The verification then succeeds because it uses:

  • The algorithm (alg) from the JWT header
  • The signed content from the JWT body
  • The signature from the JWT footer
  • The public key fetched from the attacker’s JWKS
SignatureVerifier.verify(
  token.header.alg,
  token.signedPayload,
  token.signature,
  key_from(JWKS(iss))
)

How JWKS Is Retrieved by Common Providers

How JWKS is retrieved differs by provider, but the trust decision is the same:

  • Amazon Cognito — the user pool ID differentiates your Cognito issuer (e.g., https://cognito-idp.<region>.amazonaws.com/<userPoolId>)
  • Auth0 — discovery at https://{yourDomain}/.well-known/openid-configuration returns a jwks_uri specific to your tenant
  • Okta — discovery at https://{yourOktaDomain}/oauth2/default/.well-known/openid-configuration (or a custom AS) exposes jwks_uri

The issue occurs if the application implicitly trusts the discovery URL derived from the iss claim and uses libraries that automatically fetch and parse the JWKS from that unverified issuer.

Vulnerable Java/Spring Implementation

import com.nimbusds.jwt.SignedJWT;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import jakarta.servlet.http.HttpServletRequest;

@Bean
AuthenticationManagerResolver<HttpServletRequest> resolver() {
  return request -> {
    String token = bearer(request);           // read from Authorization: Bearer ...
    String iss = SignedJWT.parse(token)
                   .getJWTClaimsSet()
                   .getIssuer();
    String jwks = iss + "/.well-known/jwks.json";  // ⚠️ trusts attacker-controlled iss
    NimbusJwtDecoder decoder = NimbusJwtDecoder
        .withJwkSetUri(jwks)
        .build();
    JwtAuthenticationProvider provider = new JwtAuthenticationProvider(decoder);
    return provider::authenticate;
  };
}

How the Signature Bypass Works in Practice

  1. Generate a forged JWT using attacker-controlled private keys
  2. Expose a JWKS at an attacker domain (following the JWKS spec) under /.well-known/jwks.json
  3. Set the iss claim in the forged token to the attacker domain
  4. The vulnerable application reads iss, discovers the JWKS from that URL, fetches the attacker’s public key, and “verifies” the token — granting access

Root Cause

Deriving the JWKS (and therefore trust) from unverified token data (iss) instead of from pinned configuration (allowed issuers/tenants and their known JWKS endpoints).


Remediation

Never derive the JWKS URL from the iss claim in the token itself. Instead:

  • Pin your issuer(s) — maintain a static allowlist of trusted issuers and their corresponding JWKS URIs in your application configuration
  • Validate iss before fetching keys — check that the issuer matches your expected value before making any network calls
  • Use well-maintained libraries correctly — Spring Security’s NimbusJwtDecoder is safe when configured with a hardcoded jwks-uri, not a dynamically constructed one
// ✅ Secure — issuer and JWKS URI are pinned in config, not derived from token
@Bean
JwtDecoder jwtDecoder() {
  return NimbusJwtDecoder
      .withJwkSetUri("https://your-idp.com/.well-known/jwks.json")
      .build();
}

Found a JWT misconfiguration in your application? Get in touch — our application security team specialises in authentication and authorization assessments.

Want to secure your systems?

Talk to Our Team

Every engagement starts with a free conversation about your risk profile.

Get in Touch More Articles