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

  1. Set subject_token_type to urn:ietf:params:oauth:token-type:access_token (or urn:ietf:params:oauth:token-type:jwt) exactly as spelled.
  2. 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.
  3. Verify actor_token_type, if sent, also uses one of the two supported URIs, or omit actor_token entirely.
  4. 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

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/2608d4eb4d705b55. Report an issue: GitHub.