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

  1. Parse the description after "OAuth 2.0 Parameter:" to identify the exact parameter and correct it in the request.
  2. 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).
  3. Check the client library is not duplicating parameters (inspect the raw request body/URL).
  4. 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

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


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