Skip to content

Commit f0a6a4c

Browse files
committed
Polish gh-1468
1 parent 4bc0df5 commit f0a6a4c

13 files changed

+128
-125
lines changed

docs/src/docs/asciidoc/examples/src/test/java/sample/AuthorizationCodeGrantFlow.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.oidc.web.authentication;
1717

18+
import java.util.Map;
19+
1820
import javax.servlet.http.HttpServletRequest;
1921

2022
import org.springframework.http.converter.HttpMessageConverter;
@@ -30,6 +32,8 @@
3032
import org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;
3133
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcClientRegistrationEndpointFilter;
3234
import org.springframework.security.web.authentication.AuthenticationConverter;
35+
import org.springframework.util.LinkedMultiValueMap;
36+
import org.springframework.util.MultiValueMap;
3337
import org.springframework.util.StringUtils;
3438

3539
/**
@@ -65,14 +69,30 @@ public Authentication convert(HttpServletRequest request) {
6569
return new OidcClientRegistrationAuthenticationToken(principal, clientRegistration);
6670
}
6771

72+
MultiValueMap<String, String> parameters = getQueryParameters(request);
73+
6874
// client_id (REQUIRED)
69-
String clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID);
75+
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
7076
if (!StringUtils.hasText(clientId) ||
71-
request.getParameterValues(OAuth2ParameterNames.CLIENT_ID).length != 1) {
77+
parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
7278
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
7379
}
7480

7581
return new OidcClientRegistrationAuthenticationToken(principal, clientId);
7682
}
7783

84+
private static MultiValueMap<String, String> getQueryParameters(HttpServletRequest request) {
85+
Map<String, String[]> parameterMap = request.getParameterMap();
86+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
87+
parameterMap.forEach((key, values) -> {
88+
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
89+
if (queryString.contains(key) && values.length > 0) {
90+
for (String value : values) {
91+
parameters.add(key, value);
92+
}
93+
}
94+
});
95+
return parameters;
96+
}
97+
7898
}

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

-12
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.springframework.security.core.Authentication;
2424
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
2525
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
26-
import org.springframework.security.oauth2.core.OAuth2Error;
2726
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
2827
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
2928
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
@@ -48,17 +47,6 @@ public final class ClientSecretPostAuthenticationConverter implements Authentica
4847
@Nullable
4948
@Override
5049
public Authentication convert(HttpServletRequest request) {
51-
String queryString = request.getQueryString();
52-
if (StringUtils.hasText(queryString) &&
53-
(queryString.contains(OAuth2ParameterNames.CLIENT_ID) ||
54-
queryString.contains(OAuth2ParameterNames.CLIENT_SECRET))) {
55-
OAuth2Error error = new OAuth2Error(
56-
OAuth2ErrorCodes.INVALID_REQUEST,
57-
"Client credentials MUST NOT be included in the request URI.",
58-
null);
59-
throw new OAuth2AuthenticationException(error);
60-
}
61-
6250
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);
6351

6452
// client_id (REQUIRED)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2021 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

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

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public final class OAuth2AuthorizationCodeAuthenticationConverter implements Aut
4848
@Override
4949
public Authentication convert(HttpServletRequest request) {
5050
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);
51+
5152
// grant_type (REQUIRED)
5253
String grantType = parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE);
5354
if (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(grantType)) {

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ public Authentication convert(HttpServletRequest request) {
6666
return null;
6767
}
6868

69-
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getQueryParameters(request);
69+
MultiValueMap<String, String> parameters =
70+
"GET".equals(request.getMethod()) ?
71+
OAuth2EndpointUtils.getQueryParameters(request) :
72+
OAuth2EndpointUtils.getFormParameters(request);
7073

7174
// response_type (REQUIRED)
7275
String responseType = parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE);

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

+12-10
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,17 @@
3939
*/
4040
final class OAuth2EndpointUtils {
4141
static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
42+
4243
private OAuth2EndpointUtils() {
4344
}
4445

4546
static MultiValueMap<String, String> getFormParameters(HttpServletRequest request) {
4647
Map<String, String[]> parameterMap = request.getParameterMap();
4748
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
4849
parameterMap.forEach((key, values) -> {
50+
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
4951
// If not query parameter then it's a form parameter
50-
if ((!StringUtils.hasText(request.getQueryString()) && values.length > 0)
51-
|| (!request.getQueryString().contains(key) && values.length > 0)) {
52+
if (!queryString.contains(key) && values.length > 0) {
5253
for (String value : values) {
5354
parameters.add(key, value);
5455
}
@@ -61,8 +62,8 @@ static MultiValueMap<String, String> getQueryParameters(HttpServletRequest reque
6162
Map<String, String[]> parameterMap = request.getParameterMap();
6263
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
6364
parameterMap.forEach((key, values) -> {
64-
if (StringUtils.hasText(request.getQueryString())
65-
&& request.getQueryString().contains(key) && values.length > 0) {
65+
String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : "";
66+
if (queryString.contains(key) && values.length > 0) {
6667
for (String value : values) {
6768
parameters.add(key, value);
6869
}
@@ -75,7 +76,10 @@ static Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(H
7576
if (!matchesAuthorizationCodeGrantRequest(request)) {
7677
return Collections.emptyMap();
7778
}
78-
MultiValueMap<String, String> multiValueParameters = getFormParameters(request);
79+
MultiValueMap<String, String> multiValueParameters =
80+
"GET".equals(request.getMethod()) ?
81+
getQueryParameters(request) :
82+
getFormParameters(request);
7983
for (String exclusion : exclusions) {
8084
multiValueParameters.remove(exclusion);
8185
}
@@ -88,16 +92,14 @@ static Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(H
8892
}
8993

9094
static boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) {
91-
MultiValueMap<String, String> parameters = getFormParameters(request);
9295
return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(
93-
parameters.getFirst(OAuth2ParameterNames.GRANT_TYPE)) &&
94-
parameters.getFirst(OAuth2ParameterNames.CODE) != null;
96+
request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) &&
97+
request.getParameter(OAuth2ParameterNames.CODE) != null;
9598
}
9699

97100
static boolean matchesPkceTokenRequest(HttpServletRequest request) {
98-
MultiValueMap<String, String> parameters = getFormParameters(request);
99101
return matchesAuthorizationCodeGrantRequest(request) &&
100-
parameters.getFirst(PkceParameterNames.CODE_VERIFIER) != null;
102+
request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
101103
}
102104

103105
static void throwError(String errorCode, String parameterName, String errorUri) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ public Authentication convert(HttpServletRequest request) {
5353
return null;
5454
}
5555

56-
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getFormParameters(request);
56+
MultiValueMap<String, String> parameters =
57+
"GET".equals(request.getMethod()) ?
58+
OAuth2EndpointUtils.getQueryParameters(request) :
59+
OAuth2EndpointUtils.getFormParameters(request);
5760

5861
// client_id (REQUIRED for public clients)
5962
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java

-33
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
import org.springframework.security.oauth2.core.AuthorizationGrantType;
6060
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
6161
import org.springframework.security.oauth2.core.OAuth2AccessToken;
62-
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
6362
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
6463
import org.springframework.security.oauth2.jose.TestJwks;
6564
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
@@ -98,7 +97,6 @@
9897
import org.springframework.security.web.util.matcher.RequestMatcher;
9998
import org.springframework.test.web.servlet.MockMvc;
10099
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
101-
import org.springframework.web.util.UriComponentsBuilder;
102100

103101
import static org.assertj.core.api.Assertions.assertThat;
104102
import static org.mockito.ArgumentMatchers.any;
@@ -232,37 +230,6 @@ public void requestWhenTokenRequestPostsClientCredentialsThenTokenResponse() thr
232230
verify(jwtCustomizer).customize(any());
233231
}
234232

235-
// gh-1378
236-
@Test
237-
public void requestWhenTokenRequestWithClientCredentialsInQueryParamThenInvalidRequest() throws Exception {
238-
this.spring.register(AuthorizationServerConfiguration.class).autowire();
239-
240-
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
241-
this.registeredClientRepository.save(registeredClient);
242-
243-
String tokenEndpointUri = UriComponentsBuilder.fromUriString(DEFAULT_TOKEN_ENDPOINT_URI)
244-
.queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
245-
.toUriString();
246-
247-
this.mvc.perform(post(tokenEndpointUri)
248-
.param(OAuth2ParameterNames.CLIENT_SECRET, registeredClient.getClientSecret())
249-
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
250-
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2"))
251-
.andExpect(status().isBadRequest())
252-
.andExpect(jsonPath("$.error").value(OAuth2ErrorCodes.INVALID_REQUEST));
253-
254-
tokenEndpointUri = UriComponentsBuilder.fromUriString(DEFAULT_TOKEN_ENDPOINT_URI)
255-
.queryParam(OAuth2ParameterNames.CLIENT_SECRET, registeredClient.getClientSecret())
256-
.toUriString();
257-
258-
this.mvc.perform(post(tokenEndpointUri)
259-
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
260-
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
261-
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2"))
262-
.andExpect(status().isBadRequest())
263-
.andExpect(jsonPath("$.error").value(OAuth2ErrorCodes.INVALID_REQUEST));
264-
}
265-
266233
@Test
267234
public void requestWhenTokenEndpointCustomizedThenUsed() throws Exception {
268235
this.spring.register(AuthorizationServerConfigurationCustomTokenEndpoint.class).autowire();

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 the original author or authors.
2+
* Copyright 2020-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@
6161
import org.springframework.security.web.authentication.AuthenticationConverter;
6262
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
6363
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
64+
import org.springframework.web.util.UriComponentsBuilder;
6465

6566
import static org.assertj.core.api.Assertions.assertThat;
6667
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -327,6 +328,7 @@ public void doFilterWhenClientConfigurationRequestEmptyClientIdThenNotProcessed(
327328
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
328329
request.setServletPath(requestUri);
329330
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "");
331+
updateQueryString(request);
330332
MockHttpServletResponse response = new MockHttpServletResponse();
331333
FilterChain filterChain = mock(FilterChain.class);
332334

@@ -342,6 +344,7 @@ public void doFilterWhenClientConfigurationRequestMultipleClientIdThenInvalidReq
342344
request.setServletPath(requestUri);
343345
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-id");
344346
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-id2");
347+
updateQueryString(request);
345348
MockHttpServletResponse response = new MockHttpServletResponse();
346349
FilterChain filterChain = mock(FilterChain.class);
347350

@@ -388,6 +391,7 @@ private void doFilterWhenClientConfigurationRequestInvalidThenError(
388391
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
389392
request.setServletPath(requestUri);
390393
request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1");
394+
updateQueryString(request);
391395
MockHttpServletResponse response = new MockHttpServletResponse();
392396
FilterChain filterChain = mock(FilterChain.class);
393397

@@ -421,6 +425,7 @@ public void doFilterWhenClientConfigurationRequestValidThenSuccessResponse() thr
421425
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
422426
request.setServletPath(requestUri);
423427
request.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId());
428+
updateQueryString(request);
424429

425430
MockHttpServletResponse response = new MockHttpServletResponse();
426431
FilterChain filterChain = mock(FilterChain.class);
@@ -463,6 +468,7 @@ public void doFilterWhenCustomAuthenticationConverterThenUsed() throws ServletEx
463468
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
464469
request.setServletPath(requestUri);
465470
request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client-id");
471+
updateQueryString(request);
466472

467473
MockHttpServletResponse response = new MockHttpServletResponse();
468474
FilterChain filterChain = mock(FilterChain.class);
@@ -492,6 +498,7 @@ public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exce
492498
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
493499
request.setServletPath(requestUri);
494500
request.setParameter(OAuth2ParameterNames.CLIENT_ID, expectedClientRegistrationResponse.getClientId());
501+
updateQueryString(request);
495502

496503
MockHttpServletResponse response = new MockHttpServletResponse();
497504
FilterChain filterChain = mock(FilterChain.class);
@@ -513,6 +520,7 @@ public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exce
513520
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
514521
request.setServletPath(requestUri);
515522
request.setParameter(OAuth2ParameterNames.CLIENT_ID, "client1");
523+
updateQueryString(request);
516524
MockHttpServletResponse response = new MockHttpServletResponse();
517525
FilterChain filterChain = mock(FilterChain.class);
518526

@@ -522,6 +530,18 @@ public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exce
522530
any(OAuth2AuthenticationException.class));
523531
}
524532

533+
private static void updateQueryString(MockHttpServletRequest request) {
534+
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(request.getRequestURI());
535+
request.getParameterMap().forEach((key, values) -> {
536+
if (values.length > 0) {
537+
for (String value : values) {
538+
uriBuilder.queryParam(key, value);
539+
}
540+
}
541+
});
542+
request.setQueryString(uriBuilder.build().getQuery());
543+
}
544+
525545
private OAuth2Error readError(MockHttpServletResponse response) throws Exception {
526546
MockClientHttpResponse httpResponse = new MockClientHttpResponse(
527547
response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus()));

0 commit comments

Comments
 (0)