Skip to content

Commit 75c3a52

Browse files
committed
Client credentials are not allowed in query parameters
Closes gh-1378
1 parent 6d21a65 commit 75c3a52

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

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

+13-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.
@@ -23,6 +23,7 @@
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;
2627
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
2728
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
2829
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
@@ -69,6 +70,17 @@ public Authentication convert(HttpServletRequest request) {
6970
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
7071
}
7172

73+
String queryString = request.getQueryString();
74+
if (StringUtils.hasText(queryString) &&
75+
(queryString.contains(OAuth2ParameterNames.CLIENT_ID) ||
76+
queryString.contains(OAuth2ParameterNames.CLIENT_SECRET))) {
77+
OAuth2Error error = new OAuth2Error(
78+
OAuth2ErrorCodes.INVALID_REQUEST,
79+
"Client credentials MUST NOT be included in the request URI.",
80+
null);
81+
throw new OAuth2AuthenticationException(error);
82+
}
83+
7284
Map<String, Object> additionalParameters = OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request,
7385
OAuth2ParameterNames.CLIENT_ID,
7486
OAuth2ParameterNames.CLIENT_SECRET);

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

+34-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.
@@ -59,6 +59,7 @@
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;
6263
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
6364
import org.springframework.security.oauth2.jose.TestJwks;
6465
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
@@ -97,6 +98,7 @@
9798
import org.springframework.security.web.util.matcher.RequestMatcher;
9899
import org.springframework.test.web.servlet.MockMvc;
99100
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
101+
import org.springframework.web.util.UriComponentsBuilder;
100102

101103
import static org.assertj.core.api.Assertions.assertThat;
102104
import static org.mockito.ArgumentMatchers.any;
@@ -230,6 +232,37 @@ public void requestWhenTokenRequestPostsClientCredentialsThenTokenResponse() thr
230232
verify(jwtCustomizer).customize(any());
231233
}
232234

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+
233266
@Test
234267
public void requestWhenTokenEndpointCustomizedThenUsed() throws Exception {
235268
this.spring.register(AuthorizationServerConfigurationCustomTokenEndpoint.class).autowire();

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java

+25
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ public void convertWhenMultipleClientSecretsThenInvalidRequestError() {
7979
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
8080
}
8181

82+
// gh-1378
83+
@Test
84+
public void convertWhenClientCredentialsInQueryParamThenInvalidRequestError() {
85+
MockHttpServletRequest request = new MockHttpServletRequest();
86+
request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1");
87+
request.addParameter(OAuth2ParameterNames.CLIENT_SECRET, "client-secret");
88+
request.setQueryString("client_id=client-1");
89+
assertThatThrownBy(() -> this.converter.convert(request))
90+
.isInstanceOf(OAuth2AuthenticationException.class)
91+
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
92+
.satisfies(error -> {
93+
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
94+
assertThat(error.getDescription()).isEqualTo("Client credentials MUST NOT be included in the request URI.");
95+
});
96+
97+
request.setQueryString("client_secret=client-secret");
98+
assertThatThrownBy(() -> this.converter.convert(request))
99+
.isInstanceOf(OAuth2AuthenticationException.class)
100+
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
101+
.satisfies(error -> {
102+
assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
103+
assertThat(error.getDescription()).isEqualTo("Client credentials MUST NOT be included in the request URI.");
104+
});
105+
}
106+
82107
@Test
83108
public void convertWhenPostWithValidCredentialsThenReturnClientAuthenticationToken() {
84109
MockHttpServletRequest request = new MockHttpServletRequest();

0 commit comments

Comments
 (0)