Browse Source

Missing response_type in POST authorization request returns invalid_request

Issue https://github.com/spring-projects/spring-authorization-server/issues/2226
pull/18076/head
Joe Grandja 2 months ago
parent
commit
90855aa128
  1. 23
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java
  2. 12
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java
  3. 15
      oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java
  4. 15
      oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java

23
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java

@ -40,6 +40,7 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; 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.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
@ -151,18 +152,24 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte
.matcher(HttpMethod.GET, authorizationEndpointUri); .matcher(HttpMethod.GET, authorizationEndpointUri);
RequestMatcher authorizationRequestPostMatcher = PathPatternRequestMatcher.withDefaults() RequestMatcher authorizationRequestPostMatcher = PathPatternRequestMatcher.withDefaults()
.matcher(HttpMethod.POST, authorizationEndpointUri); .matcher(HttpMethod.POST, authorizationEndpointUri);
RequestMatcher authorizationConsentMatcher = createAuthorizationConsentMatcher(authorizationEndpointUri);
RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher, RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher,
new AndRequestMatcher(authorizationRequestPostMatcher, responseTypeParameterMatcher)); new AndRequestMatcher(authorizationRequestPostMatcher,
RequestMatcher authorizationConsentMatcher = new AndRequestMatcher(authorizationRequestPostMatcher, new NegatedRequestMatcher(authorizationConsentMatcher)));
new NegatedRequestMatcher(responseTypeParameterMatcher));
return new OrRequestMatcher(authorizationRequestMatcher, 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 @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {

12
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java

@ -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.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2PushedAuthorizationRequestEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2PushedAuthorizationRequestEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter; 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.security.web.util.matcher.RequestMatcher;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
@ -198,12 +196,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme
} }
private static RequestMatcher createDefaultRequestMatcher() { private static RequestMatcher createDefaultRequestMatcher() {
RequestMatcher getMethodMatcher = (request) -> "GET".equals(request.getMethod()); final RequestMatcher authorizationConsentMatcher = OAuth2AuthorizationConsentAuthenticationConverter
RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod()); .createDefaultRequestMatcher();
RequestMatcher responseTypeParameterMatcher = ( return (request) -> "GET".equals(request.getMethod())
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; || ("POST".equals(request.getMethod()) && !authorizationConsentMatcher.matches(request));
return new OrRequestMatcher(getMethodMatcher,
new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher));
} }
private static void throwError(String errorCode, String parameterName) { private static void throwError(String errorCode, String parameterName) {

15
oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java

@ -30,12 +30,11 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; 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.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter; import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.web.authentication.AuthenticationConverter; 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.security.web.util.matcher.RequestMatcher;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -106,11 +105,13 @@ public final class OAuth2AuthorizationConsentAuthenticationConverter implements
additionalParameters); additionalParameters);
} }
private static RequestMatcher createDefaultRequestMatcher() { static RequestMatcher createDefaultRequestMatcher() {
RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod()); return (request) -> "POST".equals(request.getMethod())
RequestMatcher responseTypeParameterMatcher = ( && request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) == null
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; && request.getParameter(OAuth2ParameterNames.REQUEST_URI) == null
return new AndRequestMatcher(postMethodMatcher, new NegatedRequestMatcher(responseTypeParameterMatcher)); && 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) { private static void throwError(String errorCode, String parameterName) {

15
oauth2/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java

@ -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 @Test
public void doFilterWhenAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception { public void doFilterWhenAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception {
doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(),
@ -646,7 +657,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
this.filter.doFilter(request, response, filterChain); this.filter.doFilter(request, response, filterChain);
verify(this.authenticationManager).authenticate(any()); verify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));
verifyNoInteractions(filterChain); verifyNoInteractions(filterChain);
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());
@ -675,7 +686,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
this.filter.doFilter(request, response, filterChain); this.filter.doFilter(request, response, filterChain);
verify(this.authenticationManager).authenticate(any()); verify(this.authenticationManager).authenticate(any(OAuth2AuthorizationCodeRequestAuthenticationToken.class));
verifyNoInteractions(filterChain); verifyNoInteractions(filterChain);
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());

Loading…
Cancel
Save