quarkusio/quarkus · error · io.quarkus.oidc.runtime.OIDCException

Cannot access the %s endpoint for client '%s' because a JWT

Error message

Cannot access the %s endpoint for client '%s' because a JWT bearer client_assertion is not available

What it means

Thrown by OidcProviderClientImpl when building an outgoing request that must use JWT-bearer client authentication (credentials.jwt.source=BEARER) but the async JWT bearer client_assertion is null at request time. The endpoint call cannot be authorized (no Authorization: Bearer header can be set), so an OIDCException naming the operation and client is thrown.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProviderClientImpl.java:702

        setHttpAuthorization(request, clientCredentials, oidcConfig, MetadataOperation.DISCOVERY, asyncCredentials);
    }

    static PreparedHttpRequest.CredentialsToRetry setHttpAuthorizationForJwks(HttpRequest<Buffer> request,
            ClientCredentials clientCredentials, OidcClientCommonConfig oidcConfig, AsyncCredentials asyncCredentials) {
        return setHttpAuthorization(request, clientCredentials, oidcConfig, MetadataOperation.JWKS, asyncCredentials);
    }

    private static PreparedHttpRequest.CredentialsToRetry setHttpAuthorization(HttpRequest<Buffer> request,
            ClientCredentials clientCredentials, OidcClientCommonConfig oidcConfig, MetadataOperation op,
            AsyncCredentials asyncCredentials) {
        if (clientCredentials.clientSecretBasicAuthScheme != null) {
            request.putHeader(AUTHORIZATION_HEADER, clientCredentials.clientSecretBasicAuthScheme);
            return PreparedHttpRequest.CredentialsToRetry.CLIENT_SECRET_BASIC_AUTH_SCHEME;
        } else if (clientCredentials.jwtAssertionProvided && clientCredentials.clientAssertionProvider != null
                && oidcConfig.credentials().jwt().source() == OidcClientCommonConfig.Credentials.Jwt.Source.BEARER) {
            final String clientAssertion = asyncCredentials.clientAssertion;
            if (clientAssertion == null) {
                throw new OIDCException(String.format(
                        "Cannot access the %s endpoint for client '%s' because a JWT bearer client_assertion is not available",
                        op.operation(), oidcConfig.clientId().orElse(null)));
            }
            request.putHeader(AUTHORIZATION_HEADER, OidcConstants.BEARER_SCHEME + " " + clientAssertion);
        }
        return null;
    }

    private Uni<HttpResponse<Buffer>> withCredentialsRetry(PreparedHttpRequest preparedRequest,
            Supplier<PreparedHttpRequest> refreshRequestSupplier) {
        return preparedRequest.httpRequestUni.flatMap(httpResponse -> {
            if (httpResponse.statusCode() == 401) {
                // here we need to deal with error responses (like unauthorized_client) possibly caused by
                // invalid credentialsToRetry; if credentialsToRetry provider updated credentialsToRetry, we should retry
                var credentialsRefresh = switch (preparedRequest.credentialsToRetry) {
                    case CLIENT_SECRET -> OidcCommonUtils.clientSecret(oidcConfig.credentials())
                            .map(newClientSecret -> {
                                if (newClientSecret != null && !newClientSecret.equals(clientSecret)) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Verify quarkus.oidc.credentials.jwt.key / key-file / key-store-file resolve to valid signing material and are readable at runtime
  2. Check logs for the assertion provider's earlier failure (secret fetch, key load) and fix that root cause
  3. Confirm credentials.jwt.audience matches the authorization server's expected assertion audience
  4. Temporarily switch to credentials.jwt.source=client or secret basic auth to validate the rest of the flow, then restore bearer

Example fix

# before: bearer source with no key
quarkus.oidc.credentials.jwt.source=bearer
# after
quarkus.oidc.credentials.jwt.source=bearer
quarkus.oidc.credentials.jwt.key-store-file=/secrets/keystore.p12
quarkus.oidc.credentials.jwt.key-store-password=${KEYSTORE_PW}
quarkus.oidc.credentials.jwt.key-id=token-key
quarkus.oidc.credentials.jwt.audience=https://idp.example.com/protocol/openid-connect/token
Defensive patterns

Strategy: validation

Validate before calling

// assert bearer assertion material exists before building the request
if (oidcConfig.credentials().jwt().source() == Jwt.Source.BEARER
    && (asyncCredentials.clientAssertion == null || asyncCredentials.clientAssertion.isBlank())) {
    throw new IllegalStateException("Bearer client assertion unavailable; check jwt key configuration");
}

Type guard

boolean bearerAssertionReady(Credentials cfg, String assertion) {
    return cfg.jwt().source() != Jwt.Source.BEARER || (assertion != null && !assertion.isBlank());
}

Try / catch

try {
    return client.send(op);
} catch (OIDCException e) {
    if (e.getMessage().contains("JWT bearer client_assertion is not available")) {
        asyncCredentials.refreshAssertion();
        return client.send(op);
    }
    throw e;
}

Prevention

When it happens

Trigger: Preparing a request to an OIDC operation (e.g. token, revocation, userinfo) with credentials.jwt.source=BEARER configured and a clientAssertionProvider present, while AsyncCredentials.clientAssertion is null — the JWT bearer assertion was never fetched or refresh failed.

Common situations: Private-key JWT client authentication (RFC 7523) with a missing/invalid signing key configuration; expired assertion whose renewal failed; secret-manager outages leaving the assertion unpopulated; misordering where the request fires before assertion provisioning completes.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/7744385128f9c43f. Report an issue: GitHub.