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

Cannot get token for tenant '%s' because a %s client_asserti

Error message

Cannot get token for tenant '%s' because a %s client_assertion is not available

What it means

Thrown by OidcProviderClientImpl.prepareHttpRequest while building a token-request form body when the configuration declares a JWT client assertion (credentials.jwt.source set, jwtAssertionProvided=true) but the asynchronously-fetched clientAssertion string is null at request time. The token endpoint call cannot be authenticated without the assertion, so an OIDCException naming the tenant and assertion type is thrown.

Source

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

        if (!clientSecretQueryAuthentication) {
            request.putHeader(CONTENT_TYPE_HEADER, APPLICATION_X_WWW_FORM_URLENCODED);
            request.putHeader(ACCEPT_HEADER, APPLICATION_JSON);

            if (isIntrospection(op) && introspectionBasicAuthScheme != null) {
                request.putHeader(AUTHORIZATION_HEADER, introspectionBasicAuthScheme);
                if (oidcConfig.clientId().isPresent() && oidcConfig.introspectionCredentials().includeClientId()) {
                    formBody.set(OidcConstants.CLIENT_ID, oidcConfig.clientId().get());
                }
            } else if (clientSecretBasicAuthScheme != null) {
                request.putHeader(AUTHORIZATION_HEADER, clientSecretBasicAuthScheme);
                if (hasClientSecretProvider()) {
                    credentialsToRetry = PreparedHttpRequest.CredentialsToRetry.CLIENT_SECRET_BASIC_AUTH_SCHEME;
                }
            } else if (jwtAssertionProvided) {
                final String clientAssertion = asyncCredentials.clientAssertion;
                if (clientAssertion == null) {
                    throw new OIDCException(String.format(
                            "Cannot get token for tenant '%s' because a %s client_assertion is not available",
                            oidcConfig.tenantId().get(),
                            OidcCommonUtils.getClientAssertionTokenType(oidcConfig.credentials().jwt().source())));
                }
                formBody.add(OidcConstants.CLIENT_ASSERTION, clientAssertion);
                formBody.add(OidcConstants.CLIENT_ASSERTION_TYPE, clientAssertionProvider.getClientAssertionType());
            } else if (clientJwtKey != null) {
                String jwt = OidcCommonUtils.signJwtWithKey(oidcConfig, metadata.getTokenUri(), clientJwtKey);
                if (OidcCommonUtils.isClientSecretPostJwtAuthRequired(oidcConfig.credentials())) {
                    formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId().get());
                    formBody.add(OidcConstants.CLIENT_SECRET, jwt);
                } else {
                    formBody.add(OidcConstants.CLIENT_ASSERTION_TYPE, OidcConstants.JWT_BEARER_CLIENT_ASSERTION_TYPE);
                    formBody.add(OidcConstants.CLIENT_ASSERTION, jwt);
                }
            } else if (OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials())) {
                formBody.add(OidcConstants.CLIENT_ID, oidcConfig.clientId().get());
                formBody.add(OidcConstants.CLIENT_SECRET, clientSecret);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Check quarkus.oidc.credentials.jwt configuration completeness: key, key-file, key-store-file, or secret must be valid and loadable at startup
  2. Look for earlier startup/log errors from the assertion provider (secret manager, key store) that left clientAssertion null
  3. If jwt.source=assertion, ensure the assertion is actually supplied/refreshed by the integration in use
  4. Test with credentials.jwt.source=client or secret-based auth to isolate whether the assertion pipeline is the problem

Example fix

# before: no signing material configured
quarkus.oidc.credentials.jwt.source=assertion
# after: supply key material
quarkus.oidc.credentials.jwt.key-file=/secrets/client-key.pem
quarkus.oidc.credentials.jwt.key-id=client-key
quarkus.oidc.credentials.jwt.signature-algorithm=RS256
Defensive patterns

Strategy: validation

Validate before calling

// before issuing token requests, assert the client assertion credential is resolvable
if (config.credentials().jwt().source() != null && config.credentials().jwt().key().isEmpty()
    && config.credentials().jwt().keyFile().isEmpty()
    && config.credentials().jwt().keyStoreFile().isEmpty()) {
    throw new IllegalStateException("JWT assertion source configured but no signing key material");
}

Try / catch

try {
    return oidcProvider.getToken(...);
} catch (OIDCException e) {
    if (e.getMessage().contains("client_assertion is not available")) {
        // refresh assertion credentials then retry once
        asyncCredentials.refresh();
        return oidcProvider.getToken(...);
    }
    throw e;
}

Prevention

When it happens

Trigger: A token request (code-to-token, refresh, etc.) for a tenant whose credentials use a client JWT assertion, and AsyncCredentials.clientAssertion has not been populated — typically because the assertion provider failed to produce or refresh the assertion before the HTTP request was sent.

Common situations: Misconfigured quarkus.oidc.credentials.jwt.* (e.g. missing key store, key file, or secret used to sign the assertion); assertion fetch from an external secret manager failing silently; race where the assertion expired and regeneration failed; using jwt.source=assertion without supplying the assertion.

Related errors


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