Description
Description
After upgrading from Spring Boot 2.x to Spring Boot 3.x with JDK 17, I encountered an issue with the OAuth2 refresh token functionality. The token refresh operation fails because of a significant change in how request parameters are processed in the newer version of Spring Security OAuth2 Authorization Server.
Problem Details
The issue occurs in OAuth2RefreshTokenAuthenticationConverter#convert
method. In version 1.4.2, the implementation uses OAuth2EndpointUtils.getFormParameters()
instead of the previous OAuth2EndpointUtils.getParameters()
method, which has a different implementation logic:
Previous implementation (0.4.2):
static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}
Current implementation (1.4.2):
static MultiValueMap<String, String> getFormParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameterMap.forEach((key, values) -> {
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
// If not query parameter then it's a form parameter
if (!queryString.contains(key) && values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}
Root Cause
The new implementation attempts to distinguish between query parameters and form parameters by checking if the parameter key exists in the query string. However, this causes a critical issue:
If the request's query string contains the same parameters as the parameter map (which can happen when parameters are sent in the URL rather than in the request body), the parameters
MultiValueMap will be empty. As a result, String grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)
returns null, causing the refresh token operation to fail.
Environment
- Spring Boot: 3.x
- Spring Security OAuth2 Authorization Server: 1.4.2
- JDK: 17
Expected Behavior
The refresh token functionality should work regardless of how the parameters are sent (query string or form body).
Possible Solution
Consider revising the getFormParameters
method to properly handle cases where parameters might be present in both the query string and form body, or provide a more robust way to extract the required parameters for token refresh operations.