1
0
mirror of synced 2026-05-22 21:33:16 +00:00

Missing response_type in POST authorization request returns invalid_request

Issue https://github.com/spring-projects/spring-authorization-server/issues/2226
This commit is contained in:
Joe Grandja
2025-10-24 05:46:00 -04:00
parent 9f7e92d6f2
commit 90855aa128
4 changed files with 40 additions and 25 deletions
@@ -40,6 +40,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
@@ -151,18 +152,24 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte
.matcher(HttpMethod.GET, authorizationEndpointUri);
RequestMatcher authorizationRequestPostMatcher = PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.POST, authorizationEndpointUri);
RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
RequestMatcher authorizationConsentMatcher = createAuthorizationConsentMatcher(authorizationEndpointUri);
RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher,
new AndRequestMatcher(authorizationRequestPostMatcher, responseTypeParameterMatcher));
RequestMatcher authorizationConsentMatcher = new AndRequestMatcher(authorizationRequestPostMatcher,
new NegatedRequestMatcher(responseTypeParameterMatcher));
new AndRequestMatcher(authorizationRequestPostMatcher,
new NegatedRequestMatcher(authorizationConsentMatcher)));
return new OrRequestMatcher(authorizationRequestMatcher, authorizationConsentMatcher);
}
private static RequestMatcher createAuthorizationConsentMatcher(String authorizationEndpointUri) {
final RequestMatcher authorizationConsentPostMatcher = PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.POST, authorizationEndpointUri);
return (request) -> authorizationConsentPostMatcher.matches(request)
&& request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) == null
&& request.getParameter(OAuth2ParameterNames.REQUEST_URI) == null
&& request.getParameter(OAuth2ParameterNames.REDIRECT_URI) == null
&& request.getParameter(PkceParameterNames.CODE_CHALLENGE) == null
&& request.getParameter(PkceParameterNames.CODE_CHALLENGE_METHOD) == null;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
@@ -44,8 +44,6 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2PushedAuthorizationRequestEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
@@ -198,12 +196,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme
}
private static RequestMatcher createDefaultRequestMatcher() {
RequestMatcher getMethodMatcher = (request) -> "GET".equals(request.getMethod());
RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod());
RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
return new OrRequestMatcher(getMethodMatcher,
new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher));
final RequestMatcher authorizationConsentMatcher = OAuth2AuthorizationConsentAuthenticationConverter
.createDefaultRequestMatcher();
return (request) -> "GET".equals(request.getMethod())
|| ("POST".equals(request.getMethod()) && !authorizationConsentMatcher.matches(request));
}
private static void throwError(String errorCode, String parameterName) {
@@ -30,12 +30,11 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
@@ -106,11 +105,13 @@ public final class OAuth2AuthorizationConsentAuthenticationConverter implements
additionalParameters);
}
private static RequestMatcher createDefaultRequestMatcher() {
RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod());
RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
return new AndRequestMatcher(postMethodMatcher, new NegatedRequestMatcher(responseTypeParameterMatcher));
static RequestMatcher createDefaultRequestMatcher() {
return (request) -> "POST".equals(request.getMethod())
&& request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) == null
&& request.getParameter(OAuth2ParameterNames.REQUEST_URI) == null
&& request.getParameter(OAuth2ParameterNames.REDIRECT_URI) == null
&& request.getParameter(PkceParameterNames.CODE_CHALLENGE) == null
&& request.getParameter(PkceParameterNames.CODE_CHALLENGE_METHOD) == null;
}
private static void throwError(String errorCode, String parameterName) {
@@ -207,6 +207,17 @@ public class OAuth2AuthorizationEndpointFilterTests {
});
}
// gh-2226
@Test
public void doFilterWhenPostAuthorizationRequestMissingResponseTypeThenInvalidRequestError() throws Exception {
doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),
OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, (request) -> {
request.setMethod("POST");
request.removeParameter(OAuth2ParameterNames.RESPONSE_TYPE);
request.setQueryString(null);
});
}
@Test
public void doFilterWhenAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception {
doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),
@@ -646,7 +657,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
this.filter.doFilter(request, response, filterChain);
verify(this.authenticationManager).authenticate(any());
verify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));
verifyNoInteractions(filterChain);
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());
@@ -675,7 +686,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
this.filter.doFilter(request, response, filterChain);
verify(this.authenticationManager).authenticate(any());
verify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));
verifyNoInteractions(filterChain);
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());