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
- Verify quarkus.oidc.credentials.jwt.key / key-file / key-store-file resolve to valid signing material and are readable at runtime
- Check logs for the assertion provider's earlier failure (secret fetch, key load) and fix that root cause
- Confirm credentials.jwt.audience matches the authorization server's expected assertion audience
- 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
- Ensure key-store/key-file paths are valid in the target environment (container volumes, permissions)
- Set assertion renewal well before expiry
- Log assertion provisioning failures at startup rather than letting them surface at request time
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
- The '%s' annotation is only supported when proactive authent
- Client certificate thumbprint is not available
- DPoP access token does not contain a confirmation 'cnf' clai
- DPoP access token hash does not match the DPoP proof access
- Cannot get token for tenant '%s' because a %s client_asserti
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7744385128f9c43f.
Report an issue: GitHub.