spring-projects/spring-security · error · OAuth2AuthenticationException
OAuth 2.0 Parameter: ${parameterName}
Error message
OAuth 2.0 Parameter: ${parameterName} What it means
OAuth2EndpointUtils.throwError is the shared helper used by endpoint converters/validators to raise OAuth2AuthenticationException with description "OAuth 2.0 Parameter: <parameterName>" whenever a required OAuth 2.0 endpoint parameter is missing, malformed, or duplicated. It is used across token/device endpoints (e.g. during DPoP parameter validation in validateAndAddDPoPParametersIfAvailable). The description always names the exact failing parameter.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java:128
static void validateAndAddDPoPParametersIfAvailable(HttpServletRequest request,
Map<String, Object> additionalParameters) {
final String dPoPProofHeaderName = OAuth2AccessToken.TokenType.DPOP.getValue();
String dPoPProof = request.getHeader(dPoPProofHeaderName);
if (StringUtils.hasText(dPoPProof)) {
if (Collections.list(request.getHeaders(dPoPProofHeaderName)).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, dPoPProofHeaderName, ACCESS_TOKEN_REQUEST_ERROR_URI);
}
else {
additionalParameters.put("dpop_proof", dPoPProof);
additionalParameters.put("dpop_method", request.getMethod());
additionalParameters.put("dpop_target_uri", request.getRequestURL().toString());
}
}
}
static void throwError(String errorCode, String parameterName, String errorUri) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
throw new OAuth2AuthenticationException(error);
}
static String normalizeUserCode(String userCode) {
Assert.hasText(userCode, "userCode cannot be empty");
StringBuilder sb = new StringBuilder(userCode.toUpperCase(Locale.ENGLISH).replaceAll("[^A-Z\\d]+", ""));
Assert.isTrue(sb.length() == 8, "userCode must be exactly 8 alpha/numeric characters");
sb.insert(4, '-');
return sb.toString();
}
static boolean validateUserCode(String userCode) {
return (userCode != null && userCode.toUpperCase(Locale.ENGLISH).replaceAll("[^A-Z\\d]+", "").length() == 8);
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Parse the description after "OAuth 2.0 Parameter:" to identify the exact parameter and correct it in the request.
- Ensure every parameter required by the grant type is present exactly once (grant_type, code, redirect_uri, code_verifier for PKCE, device_code for device grant).
- Check the client library is not duplicating parameters (inspect the raw request body/URL).
- For DPoP, ensure the DPoP proof JWT and related parameters are well-formed and use the correct algorithm.
Example fix
// before: token request missing PKCE verifier POST /oauth2/token grant_type=authorization_code&code=abc&redirect_uri=... // after POST /oauth2/token grant_type=authorization_code&code=abc&redirect_uri=...&client_id=my-client&code_verifier=PLAINTEXT_VERIFIER
Defensive patterns
Strategy: validation
Validate before calling
function validateTokenRequest(params, grantType) {
const required = { 'authorization_code': ['grant_type','code','redirect_uri'],
'urn:ietf:params:oauth:grant-type:device_code': ['grant_type','device_code'] };
const need = required[grantType] || ['grant_type'];
return need.filter(k => !params.get(k) || params.getAll(k).length > 1);
}
const missingOrDup = validateTokenRequest(tokenParams, grantType);
if (missingOrDup.length) throw new Error('Bad token request params: ' + missingOrDup.join(',')); Type guard
function hasExactlyOnce(params, key) {
const all = params.getAll(key);
return all.length === 1 && all[0].length > 0;
} Try / catch
try {
tokenResponse = requestToken(tokenEndpoint, params);
} catch (OAuth2AuthenticationException e) {
OAuth2Error err = e.getError();
if (err.getDescription().startsWith("OAuth 2.0 Parameter:")) {
String param = err.getDescription().substring("OAuth 2.0 Parameter:".length()).trim();
logger.warn("Token request rejected, fix parameter: {}", param);
}
} Prevention
- Send each OAuth parameter exactly once — inspect the raw body for duplicated keys.
- Include code_verifier on PKCE authorization_code token requests.
- Match redirect_uri exactly to the one used in the authorization request.
- When using DPoP, verify proof JWT header/algorithm configuration before calling the endpoint.
When it happens
Trigger: Called when: a required parameter (grant_type, code, redirect_uri, code_verifier, device_code, etc.) is absent; a parameter value fails format validation (invalid URI, invalid scope); a parameter appears multiple times; or DPoP-related parameters are invalid during validateAndAddDPoPParametersIfAvailable.
Common situations: Token requests missing code_verifier for a PKCE flow; duplicated query/form parameters from a client library appending params twice; malformed redirect_uri in the token exchange; device authorization grant posts missing device_code; DPoP headers/parameters rejected because they are invalid.
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
- Unable to create an {OAuth2AuthorizedClientManager} bean. Ex
- invalid_dpop_proof
- invalid_scope
- oidc_provider_not_configured
- No enum constant org.springframework.security.oauth2.client.
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/3671ccfa4defc573.
Report an issue: GitHub.