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
- Check quarkus.oidc.credentials.jwt configuration completeness: key, key-file, key-store-file, or secret must be valid and loadable at startup
- Look for earlier startup/log errors from the assertion provider (secret manager, key store) that left clientAssertion null
- If jwt.source=assertion, ensure the assertion is actually supplied/refreshed by the integration in use
- 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
- Validate credentials.jwt.* config at startup (fail fast if key material missing)
- Monitor secret-manager/key-store health if assertions come from external sources
- Set assertion TTLs with margin so renewal happens before expiry
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
- ISSUED_AT_INVALID_PAST
- %s OidcClient can not complete the %s grant request because
- Only one of client secret or JWT secret authentication metho
- Only one of JWT secret or JWT private key authentication met
- Only one of client secret or JWT bearer/SPIFFE authenticatio
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/3932a6d3e4d365d5.
Report an issue: GitHub.