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
- Send client_assertion_type exactly once with value 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'.
- Ensure client_assertion_type is included whenever client_assertion is sent.
- Use set() not add() when building the parameter map to avoid duplicates.
- 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
- Always pair client_assertion with exactly one client_assertion_type parameter.
- Build assertion request parameters in a single helper to avoid scattered additions.
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.