spring-projects/spring-security · error · OAuth2AuthenticationException
OAuth 2.0 Token Exchange parameter: ${parameterName} - The p
Error message
OAuth 2.0 Token Exchange parameter: ${parameterName} - The provided value is not supported by this authorization server. Supported values are urn:ietf:params:oauth:token-type:access_token and urn:ietf:params:oauth:token-type:jwt. What it means
OAuth2TokenExchangeAuthenticationConverter.validateTokenType rejects a Token Exchange (RFC 8693) request whose subject_token or (if present) actor_token uses a token_type identifier this authorization server does not support. The error message explicitly lists the only accepted values: urn:ietf:params:oauth:token-type:access_token and urn:ietf:params:oauth:token-type:jwt. The resulting OAuth2AuthenticationException carries the formatted message.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenExchangeAuthenticationConverter.java:208
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
return new OAuth2TokenExchangeAuthenticationToken(requestedTokenType, subjectToken, subjectTokenType,
clientPrincipal, actorToken, actorTokenType, new LinkedHashSet<>(resources),
new LinkedHashSet<>(audiences), requestedScopes, additionalParameters);
}
private static void validateTokenType(String parameterName, String tokenTypeValue) {
if (!SUPPORTED_TOKEN_TYPES.contains(tokenTypeValue)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE,
String.format("OAuth 2.0 Token Exchange parameter: %s", parameterName), TOKEN_TYPE_IDENTIFIERS_URI);
// @formatter:off
String message = String.format(
"OAuth 2.0 Token Exchange parameter: %s - " +
"The provided value is not supported by this authorization server. " +
"Supported values are %s and %s.",
parameterName, ACCESS_TOKEN_TYPE_VALUE, JWT_TOKEN_TYPE_VALUE);
// @formatter:on
throw new OAuth2AuthenticationException(error, message);
}
}
private static boolean isValidUri(String uri) {
try {
URI validUri = new URI(uri);
return validUri.isAbsolute() && validUri.getFragment() == null;
}
catch (URISyntaxException ex) {
return false;
}
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Set subject_token_type to urn:ietf:params:oauth:token-type:access_token (or urn:ietf:params:oauth:token-type:jwt) exactly as spelled.
- If you hold an ID token, either enable id_token support server-side via a custom token exchange handler or obtain an access token first and exchange that.
- Verify actor_token_type, if sent, also uses one of the two supported URIs, or omit actor_token entirely.
- Check the server's Token Exchange configuration to confirm which token types are actually enabled before calling.
Example fix
// before
body.put("subject_token", token);
body.put("subject_token_type", "urn:ietf:params:oauth:token-type:id_token");
// after
body.put("subject_token", token);
body.put("subject_token_type", "urn:ietf:params:oauth:token-type:access_token"); Defensive patterns
Strategy: validation
Validate before calling
const SUPPORTED = ['urn:ietf:params:oauth:token-type:access_token','urn:ietf:params:oauth:token-type:jwt'];
function validateTokenExchange(body) {
const errs = [];
if (!SUPPORTED.includes(body.subject_token_type)) errs.push('subject_token_type must be ' + SUPPORTED.join(' or '));
if (body.actor_token_type && !SUPPORTED.includes(body.actor_token_type)) errs.push('actor_token_type must be ' + SUPPORTED.join(' or '));
return errs;
} Type guard
function hasSupportedTokenType(body) {
const ok = t => t === 'urn:ietf:params:oauth:token-type:access_token'
|| t === 'urn:ietf:params:oauth:token-type:jwt';
return body != null && ok(body.subject_token_type)
&& (!body.actor_token_type || ok(body.actor_token_type));
} Try / catch
try {
exchanged = tokenExchange(subjectToken, subjectTokenType);
} catch (OAuth2AuthenticationException e) {
if (e.getMessage() != null && e.getMessage().contains("Token Exchange parameter")) {
logger.warn("Unsupported token_type: {} — use access_token or jwt URIs", subjectTokenType);
// fall back to obtaining an access token first, then retry the exchange
} else throw e;
} Prevention
- Hardcode the exact URN constants; never type token-type URIs from memory.
- Exchange access tokens or JWTs only — convert ID tokens to access tokens first if the server does not accept id_token type.
- Verify server-side token exchange configuration to confirm the supported token types.
- Share the URN constants between client and provisioning code so they cannot drift.
When it happens
Trigger: A POST to the token endpoint with grant_type=urn:ietf:params:oauth:grant-type:token_exchange where subject_token_type (or actor_token_type) is set to an unsupported identifier such as urn:ietf:params:oauth:token-type:id_token, saml2, or any arbitrary string.
Common situations: Clients exchanging ID tokens instead of access tokens (id_token token-type is not enabled); SDKs defaulting to SAML or refresh_token type identifiers; misconfigured service-to-service exchange code copied from a different authorization server that supports more token types.
Related errors
- OAuth2Error from upstream authorization exception (dynamic)
- server_error
- OAuth2Error from upstream authorization exception (dynamic)
- Invalidated authorization code used by registered client '%s
- Unable to create an {OAuth2AuthorizedClientManager} bean. Ex
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/2608d4eb4d705b55.
Report an issue: GitHub.