Support POST for authorization code request flow

Closes gh-1811

Signed-off-by: sylvain-costanzo <sylvain.costanzo1@decathlon.com>
This commit is contained in:
sylvain-costanzo
2025-01-13 19:41:00 +01:00
committed by Joe Grandja
parent 1e8c463877
commit b0fca27c7b
3 changed files with 8 additions and 21 deletions

View File

@@ -39,7 +39,6 @@ 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.oidc.OidcScopes;
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,16 +150,12 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte
HttpMethod.GET.name()); HttpMethod.GET.name());
RequestMatcher authorizationRequestPostMatcher = new AntPathRequestMatcher(authorizationEndpointUri, RequestMatcher authorizationRequestPostMatcher = new AntPathRequestMatcher(authorizationEndpointUri,
HttpMethod.POST.name()); HttpMethod.POST.name());
RequestMatcher openidScopeMatcher = (request) -> {
String scope = request.getParameter(OAuth2ParameterNames.SCOPE);
return StringUtils.hasText(scope) && scope.contains(OidcScopes.OPENID);
};
RequestMatcher responseTypeParameterMatcher = ( RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher, RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher,
new AndRequestMatcher(authorizationRequestPostMatcher, responseTypeParameterMatcher, new AndRequestMatcher(authorizationRequestPostMatcher, responseTypeParameterMatcher));
openidScopeMatcher));
RequestMatcher authorizationConsentMatcher = new AndRequestMatcher(authorizationRequestPostMatcher, RequestMatcher authorizationConsentMatcher = new AndRequestMatcher(authorizationRequestPostMatcher,
new NegatedRequestMatcher(responseTypeParameterMatcher)); new NegatedRequestMatcher(responseTypeParameterMatcher));

View File

@@ -64,11 +64,11 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme
private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous",
"anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
private static final RequestMatcher OIDC_REQUEST_MATCHER = createOidcRequestMatcher(); private static final RequestMatcher POST_WITH_RESPONSE_TYPE_REQUEST_MATCHER = createPostWithResponseTypeRequestMatcher();
@Override @Override
public Authentication convert(HttpServletRequest request) { public Authentication convert(HttpServletRequest request) {
if (!"GET".equals(request.getMethod()) && !OIDC_REQUEST_MATCHER.matches(request)) { if (!"GET".equals(request.getMethod()) && !POST_WITH_RESPONSE_TYPE_REQUEST_MATCHER.matches(request)) {
return null; return null;
} }
@@ -153,15 +153,11 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme
state, scopes, additionalParameters); state, scopes, additionalParameters);
} }
private static RequestMatcher createOidcRequestMatcher() { private static RequestMatcher createPostWithResponseTypeRequestMatcher() {
RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod()); RequestMatcher postMethodMatcher = (request) -> "POST".equals(request.getMethod());
RequestMatcher responseTypeParameterMatcher = ( RequestMatcher responseTypeParameterMatcher = (
request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; request) -> request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null;
RequestMatcher openidScopeMatcher = (request) -> { return new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher);
String scope = request.getParameter(OAuth2ParameterNames.SCOPE);
return StringUtils.hasText(scope) && scope.contains(OidcScopes.OPENID);
};
return new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher, openidScopeMatcher);
} }
private static void throwError(String errorCode, String parameterName) { private static void throwError(String errorCode, String parameterName) {

View File

@@ -611,11 +611,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
@Test @Test
public void doFilterWhenAuthenticationRequestAuthenticatedThenAuthorizationResponse() throws Exception { public void doFilterWhenAuthenticationRequestAuthenticatedThenAuthorizationResponse() throws Exception {
// Setup OpenID Connect request RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(Set::clear).build();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes((scopes) -> {
scopes.clear();
scopes.add(OidcScopes.OPENID);
}).build();
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
AUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode, AUTHORIZATION_URI, registeredClient.getClientId(), this.principal, this.authorizationCode,
registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes());
@@ -623,7 +619,7 @@ public class OAuth2AuthorizationEndpointFilterTests {
given(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult); given(this.authenticationManager.authenticate(any())).willReturn(authorizationCodeRequestAuthenticationResult);
MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletRequest request = createAuthorizationRequest(registeredClient);
request.setMethod("POST"); // OpenID Connect supports POST method request.setMethod("POST");
request.setQueryString(null); request.setQueryString(null);
MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class); FilterChain filterChain = mock(FilterChain.class);