apache/pulsar · error · AuthenticationException

Malformed JWKS returned by:

Error message

Malformed JWKS returned by: 

What it means

JwksCache.convertToJwks() expects the fetched JWKS document to be a JSON object with a "keys" member holding an array of key objects (RFC 7517 section 5.1). If casting the parsed document's "keys" value to List<Map<String,Object>> throws ClassCastException — e.g. "keys" is missing (null), not a list, or contains non-map entries — the method rethrows it as AuthenticationException("Malformed JWKS returned by: <uri>").

Source

Thrown at pulsar-broker-auth-oidc/src/main/java/org/apache/pulsar/broker/authentication/oidc/JwksCache.java:243

    /**
     * The JWK Set is stored in the "keys" key see https://www.rfc-editor.org/rfc/rfc7517#section-5.1.
     *
     * @param jwksUri - the URI used to retrieve the JWKS
     * @param jwks - the JWKS to convert
     * @return a list of {@link Jwk}
     */
    private List<Jwk> convertToJwks(String jwksUri, Map<String, Object> jwks) throws AuthenticationException {
        try {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> jwkList = (List<Map<String, Object>>) jwks.get("keys");
            final List<Jwk> result = new ArrayList<>();
            for (Map<String, Object> jwk : jwkList) {
                result.add(Jwk.fromValues(jwk));
            }
            return result;
        } catch (ClassCastException e) {
            throw new AuthenticationException("Malformed JWKS returned by: " + jwksUri);
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Fetch the configured jwksUri with curl and verify it is a JWK Set: {"keys":[{...}]} with each key an object containing kty etc.
  2. Correct the jwks_uri configuration — it must point to the JWKS endpoint, not the discovery document or an HTML page.
  3. Check for proxies/ingress returning error bodies with 200 status, and bypass or fix them.
  4. If the IdP is non-standard, ensure its keyset endpoint conforms to RFC 7517 before connecting it.

Example fix

// before (wrong endpoint configured)
ownedTenant="https://idp.example.com/.well-known/openid-configuration"
// after (actual JWKS endpoint)
jwksUri=https://idp.example.com/.well-known/jwks.json
Defensive patterns

Strategy: validation

Validate before calling

curl -fsSL "$JWKS_URI" | jq -e '(.keys | type) == "array" and (.keys | length) > 0 and (.keys[0] | type) == "object"' >/dev/null \
  && echo OK || echo "Not a valid JWK Set"

Try / catch

try {
    List<Jwk> jwks = fetchAndConvert(jwksUri);
} catch (AuthenticationException e) {
    if (e.getMessage().startsWith("Malformed JWKS returned by")) {
        // verify endpoint config / check for proxy-injected error bodies
    }
}

Prevention

When it happens

Trigger: The endpoint at jwksUri returns JSON that is not a JWK Set: an error page (HTML) that still parses as generic JSON, a JSON object without a "keys" array, "keys" being a single object instead of an array, or an array of strings/numbers instead of key objects; in the Kubernetes path, the API server's .well-known/openid-configuration keyset returns an unexpected shape.

Common situations: Misconfigured jwks_uri pointing to the issuer's .well-known/openid-configuration document or a login page instead of the actual JWKS endpoint; a proxy/gateway intercepting the JWKS request and returning an error JSON body; a non-standard IdP or mocked test server serving a hand-written JWKS with a wrong structure.

Understand the failure class

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/80aebcee16638962. Report an issue: GitHub.