quarkusio/quarkus · error · ConfigurationException
Token exchange is required but OIDC client is configured to
Error message
Token exchange is required but OIDC client is configured to use the <grantType> grantType
What it means
AccessTokenRequestFilter.initExchangeTokenClient validates that the OIDC client configured for token exchange uses a grant type capable of it. Only EXCHANGE (RFC 8693) and JWT bearer grants carry an incoming token/assertion; any other grantType cannot exchange, so a ConfigurationException is thrown.
Source
Thrown at extensions/oidc-token-propagation/runtime/src/main/java/io/quarkus/oidc/token/propagation/AccessTokenRequestFilter.java:64
}
@PostConstruct
public void initExchangeTokenClient() {
if (isExchangeToken()) {
OidcClients clients = Arc.container().instance(OidcClients.class).get();
String clientName = getClientName();
exchangeTokenClient = clientName != null ? clients.getClient(clientName) : clients.getClient();
Grant.Type exchangeTokenGrantType = ConfigProvider.getConfig()
.getValue(
"quarkus.oidc-client." + (clientName != null ? clientName + "." : "")
+ "grant.type",
Grant.Type.class);
if (exchangeTokenGrantType == Grant.Type.EXCHANGE) {
exchangeTokenProperty = OidcConstants.EXCHANGE_GRANT_SUBJECT_TOKEN;
} else if (exchangeTokenGrantType == Grant.Type.JWT) {
exchangeTokenProperty = OidcConstants.JWT_BEARER_GRANT_ASSERTION;
} else {
throw new ConfigurationException("Token exchange is required but OIDC client is configured "
+ "to use the " + exchangeTokenGrantType.getGrantType() + " grantType");
}
}
}
protected boolean isExchangeToken() {
return ConfigProvider.getConfig().getValue("quarkus.resteasy-client-oidc-token-propagation.exchange-token",
boolean.class);
}
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
if (skipPropagation(requestContext)) {
return;
}
if (acquireTokenCredentialFromCtx(requestContext)) {
propagateToken(requestContext, exchangeTokenIfNeeded(getTokenCredentialFromContext().getToken()));View on GitHub (pinned to e1c734241f)
Solutions
- Set the OIDC client grant type to 'exchange' for RFC 8693 token exchange
- Or use grant type 'jwt' (JWT bearer) if exchanging via a JWT assertion
- Use a dedicated OidcClient named for exchange and point the filter to it, instead of reusing a client-credentials client
- Confirm the authorization server supports the chosen grant type
Example fix
// before quarkus.oidc-client.exchange-client.grant.type=client // after quarkus.oidc-client.exchange-client.grant.type=exchange
Defensive patterns
Strategy: validation
Validate before calling
OidcClientConfig client = clients.get("exchange-client");
Grant.Type t = client.grant().type().orElse(Grant.Type.CLIENT);
if (t != Grant.Type.EXCHANGE && t != Grant.Type.JWT) {
throw new IllegalStateException("Token exchange needs grant.type=exchange or jwt, got: " + t);
} Prevention
- Use grant.type=exchange (or jwt) for any client used for token exchange
- Dedicate a separate named OIDC client to exchange, don't reuse client-credentials clients
- Verify the IdP supports RFC 8693 token exchange
When it happens
Trigger: Token exchange is requested (exchange token configured on the OIDC client used by the filter) but the client's grant.type is something like 'client' (client credentials), 'password', or 'refresh' instead of 'exchange' or 'jwt'.
Common situations: Misconfigured quarkus.oidc-client.<name>.grant.type when wiring token exchange into REST client propagation; copying a client config that used client-credentials and trying to reuse it for exchange; authorization server not supporting the exchange grant leads to config drift.
Related errors
- The '%s' property can only be set to 'idtoken' for WEB_APP a
- '%s' must be enabled to use '%s'
- UserInfo is required but DefaultTokenStateManager is configu
- Access token is required to check the roles but DefaultToken
- UserInfo path is missing but 'verifyAccessTokenWithUserInfo'
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/d757e1b550619530.
Report an issue: GitHub.