apache/pulsar · error · AuthenticationException

UNSUPPORTED_ISSUER

UNSUPPORTED_ISSUER

Error message

Issuer not allowed: 

What it means

OpenIDProviderMetadataCache.verifyIssuer() compares the issuer being configured/looked up with the issuer claim in the fetched OpenID Provider metadata. For the Kubernetes API-server path (isK8s=true) it increments UNSUPPORTED_ISSUER and throws AuthenticationException("Issuer not allowed: <issuer>") when they differ, meaning the issuer is not acceptable to use. In the normal path the same mismatch throws an 'Issuer URL mismatch' with ISSUER_MISMATCH instead.

Source

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

    /**
     * Verify the issuer url, as required by the OpenID Connect spec:
     *
     * Per the OpenID Connect Discovery spec, the issuer value returned MUST be identical to the
     * Issuer URL that was directly used to retrieve the configuration information. This MUST also
     * be identical to the iss Claim value in ID Tokens issued from this Issuer.
     * https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation
     *
     * @param issuer - the issuer used to retrieve the metadata
     * @param metadata - the OpenID Provider Metadata
     * @param isK8s - whether the issuer is represented by the Kubernetes API server. This affects error reporting.
     * @throws AuthenticationException if the issuer does not exactly match the metadata issuer
     */
    private void verifyIssuer(@NonNull String issuer, OpenIDProviderMetadata metadata,
                              boolean isK8s) throws AuthenticationException {
        if (!issuer.equals(metadata.getIssuer())) {
            if (isK8s) {
                authenticationProvider.incrementFailureMetric(AuthenticationExceptionCode.UNSUPPORTED_ISSUER);
                throw new AuthenticationException("Issuer not allowed: " + issuer);
            } else {
                authenticationProvider.incrementFailureMetric(AuthenticationExceptionCode.ISSUER_MISMATCH);
                throw new AuthenticationException(String.format("Issuer URL mismatch: [%s] should match [%s]",
                        issuer, metadata.getIssuer()));
            }
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Compare the configured issuer with the "issuer" field of the provider metadata (curl <issuer>/.well-known/openid-configuration) and fix the broker configuration to match exactly, including scheme, host, port, path and trailing slash.
  2. In Kubernetes, align the broker's expected issuer with the API server's --service-account-issuer value (kubectl get --raw /.well-known/openid-configuration | jq -r .issuer).
  3. If the API server advertises the wrong issuer, correct --service-account-issuer (and --service-account-jwks-uri if applicable) and restart the API server.
  4. Remove trailing slashes / normalize the issuer string so both sides are byte-identical.

Example fix

// before (broker.conf)
oidcIssuer=https://kubernetes.default.svc.cluster.local/
// after — must equal the apiserver's advertised issuer exactly (no trailing slash)
oidcIssuer=https://kubernetes.default.svc.cluster.local
Defensive patterns

Strategy: validation

Validate before calling

advertised=$(curl -fsSL "$ISSUER/.well-known/openid-configuration" | jq -r .issuer)
[ "$advertised" = "$ISSUER" ] && echo OK || echo "Configured '$ISSUER' != advertised '$advertised'"

Try / catch

try {
    metadata = metadataCache.loadOpenIDProviderMetadataForIssuer(issuer);
} catch (AuthenticationException e) {
    if (e.getMessage().startsWith("Issuer not allowed: ")) {
        // fetch advertised issuer from discovery and fix configuration to match exactly
    }
}

Prevention

When it happens

Trigger: Calling loadOpenIDProviderMetadataForIssuer() or getOpenIDProviderMetadataForKubernetesApiServer() where the requested issuer string does not exactly equal the "issuer" field of the metadata document the provider/API server returned — e.g. trailing-slash differences (https://issuer/ vs https://issuer), http vs https, host/port or path differences, or a Kubernetes cluster whose API server advertises a different --service-account-issuer than the one the broker expects.

Common situations: Kubernetes: the apiserver flag --service-account-issuer was changed or set to an internal URL while the broker is configured with the external URL (or vice versa); trailing slash or case mismatch between the configured issuer and the IdP's discovery document; switching IdP environments (dev/prod) without updating the issuer; DNS/ingress causing the advertised issuer host to differ from the configured one.

Related errors


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