quarkusio/quarkus · error · io.quarkus.oidc.runtime.OIDCException
Cannot get token for tenant
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.
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
- Only one of client secret or JWT bearer/SPIFFE…
- Only one of client secret or JWT secret authentication…
- Only one of JWT private key or JWT bearer/SPIFFE…
- Only one of JWT secret or JWT private key authentication…
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/3932a6d3e4d365d5.
Report an issue: GitHub.
Appendix: 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)