spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_request

invalid_request

Error message

invalid_request

What it means

Thrown by JwtClientAssertionAuthenticationConverter.convert() when the client_assertion_type parameter is present but not exactly one value. The JWT client authentication flow requires a single client_assertion_type; duplicates trigger an immediate invalid_request before the value is even checked against 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java:65

public final class JwtClientAssertionAuthenticationConverter implements AuthenticationConverter {

	private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod(
			"urn:ietf:params:oauth:client-assertion-type:jwt-bearer");

	@Override
	public @Nullable Authentication convert(HttpServletRequest request) {
		MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);

		if (parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE) == null
				|| parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION) == null) {
			return null;
		}

		// client_assertion_type (REQUIRED)
		String clientAssertionType = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE);
		List<String> clientAssertionTypeParams = parameters.get(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE);
		if (clientAssertionTypeParams == null || clientAssertionTypeParams.size() != 1) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
		}
		if (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.getValue().equals(clientAssertionType)) {
			return null;
		}

		// client_assertion (REQUIRED)
		String jwtAssertion = parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION);
		List<String> clientAssertionParams = parameters.get(OAuth2ParameterNames.CLIENT_ASSERTION);
		if (clientAssertionParams == null || clientAssertionParams.size() != 1) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
		}

		// client_id (OPTIONAL as per specification but REQUIRED by this implementation)
		String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
		List<String> clientIdParams = parameters.get(OAuth2ParameterNames.CLIENT_ID);
		if (!StringUtils.hasText(clientId) || clientIdParams == null || clientIdParams.size() != 1) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
		}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Send client_assertion_type exactly once with value 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.
  2. Ensure client_assertion_type is included whenever client_assertion is sent.
  3. Use set() not add() when building the parameter map to avoid duplicates.
  4. Verify no query-string + body duplication of the parameter.

Example fix

// before
form.add("client_assertion", jwt); // missing client_assertion_type
// after
form.set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
form.set("client_assertion", jwt);
Defensive patterns

Strategy: validation

Validate before calling

boolean validAssertionTypeParams(MultiValueMap<String, String> form) {
    List<String> v = form.get("client_assertion_type");
    return v != null && v.size() == 1
        && "urn:ietf:params:oauth:client-assertion-type:jwt-bearer".equals(v.get(0));
}

Try / catch

try { tokenResponse = client.token(request); }
catch (OAuth2AuthenticationException e) {
    if ("invalid_request".equals(e.getError().getErrorCode())) { log.error("client_assertion_type must be present exactly once"); }
    throw e;
}

Prevention

When it happens

Trigger: Token request containing client_assertion_type twice, or zero times while some other parameter triggered converter entry (e.g. client_assertion present but assertion_type missing).

Common situations: Merging parameter maps that both contain client_assertion_type; a client library that adds the parameter while user code also adds it; omitting the parameter entirely while sending client_assertion.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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