Skip to content

Commit 799f74f

Browse files
committed
Propagate additional token request parameters
Closes spring-projectsgh-226
1 parent 835ff1d commit 799f74f

File tree

9 files changed

+115
-94
lines changed

9 files changed

+115
-94
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.security.oauth2.server.authorization.authentication;
1717

1818
import java.util.Collections;
19+
import java.util.HashMap;
1920
import java.util.Map;
2021

2122
import org.springframework.lang.Nullable;
@@ -57,7 +58,7 @@ protected OAuth2AuthorizationGrantAuthenticationToken(AuthorizationGrantType aut
5758
this.clientPrincipal = clientPrincipal;
5859
this.additionalParameters = Collections.unmodifiableMap(
5960
additionalParameters != null ?
60-
additionalParameters :
61+
new HashMap<>(additionalParameters) :
6162
Collections.emptyMap());
6263
}
6364

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

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
package org.springframework.security.oauth2.server.authorization.authentication;
1717

1818
import java.util.Collections;
19-
import java.util.LinkedHashSet;
19+
import java.util.HashSet;
20+
import java.util.Map;
2021
import java.util.Set;
2122

23+
import org.springframework.lang.Nullable;
2224
import org.springframework.security.core.Authentication;
2325
import org.springframework.security.oauth2.core.AuthorizationGrantType;
24-
import org.springframework.util.Assert;
2526

2627
/**
2728
* An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials Grant.
@@ -34,25 +35,18 @@
3435
public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
3536
private final Set<String> scopes;
3637

37-
/**
38-
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters.
39-
*
40-
* @param clientPrincipal the authenticated client principal
41-
*/
42-
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal) {
43-
this(clientPrincipal, Collections.emptySet());
44-
}
45-
4638
/**
4739
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters.
4840
*
4941
* @param clientPrincipal the authenticated client principal
5042
* @param scopes the requested scope(s)
43+
* @param additionalParameters the additional parameters
5144
*/
52-
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, Set<String> scopes) {
53-
super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, null);
54-
Assert.notNull(scopes, "scopes cannot be null");
55-
this.scopes = Collections.unmodifiableSet(new LinkedHashSet<>(scopes));
45+
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal,
46+
@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
47+
super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters);
48+
this.scopes = Collections.unmodifiableSet(
49+
scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
5650
}
5751

5852
/**

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

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
package org.springframework.security.oauth2.server.authorization.authentication;
1717

1818
import java.util.Collections;
19+
import java.util.HashSet;
20+
import java.util.Map;
1921
import java.util.Set;
2022

23+
import org.springframework.lang.Nullable;
2124
import org.springframework.security.core.Authentication;
2225
import org.springframework.security.oauth2.core.AuthorizationGrantType;
2326
import org.springframework.util.Assert;
@@ -34,30 +37,21 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr
3437
private final String refreshToken;
3538
private final Set<String> scopes;
3639

37-
/**
38-
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
39-
*
40-
* @param refreshToken the refresh token
41-
* @param clientPrincipal the authenticated client principal
42-
*/
43-
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal) {
44-
this(refreshToken, clientPrincipal, Collections.emptySet());
45-
}
46-
4740
/**
4841
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
4942
*
5043
* @param refreshToken the refresh token
5144
* @param clientPrincipal the authenticated client principal
5245
* @param scopes the requested scope(s)
46+
* @param additionalParameters the additional parameters
5347
*/
5448
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal,
55-
Set<String> scopes) {
56-
super(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, null);
49+
@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
50+
super(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, additionalParameters);
5751
Assert.hasText(refreshToken, "refreshToken cannot be empty");
58-
Assert.notNull(scopes, "scopes cannot be null");
5952
this.refreshToken = refreshToken;
60-
this.scopes = scopes;
53+
this.scopes = Collections.unmodifiableSet(
54+
scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
6155
}
6256

6357
/**

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

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ public Authentication convert(HttpServletRequest request) {
229229
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI);
230230
}
231231

232+
// @formatter:off
232233
Map<String, Object> additionalParameters = parameters
233234
.entrySet()
234235
.stream()
@@ -237,8 +238,10 @@ public Authentication convert(HttpServletRequest request) {
237238
!e.getKey().equals(OAuth2ParameterNames.CODE) &&
238239
!e.getKey().equals(OAuth2ParameterNames.REDIRECT_URI))
239240
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
241+
// @formatter:on
240242

241-
return new OAuth2AuthorizationCodeAuthenticationToken(code, clientPrincipal, redirectUri, additionalParameters);
243+
return new OAuth2AuthorizationCodeAuthenticationToken(
244+
code, clientPrincipal, redirectUri, additionalParameters);
242245
}
243246
}
244247

@@ -269,13 +272,24 @@ public Authentication convert(HttpServletRequest request) {
269272
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
270273
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE);
271274
}
275+
Set<String> requestedScopes = null;
272276
if (StringUtils.hasText(scope)) {
273-
Set<String> requestedScopes = new HashSet<>(
277+
requestedScopes = new HashSet<>(
274278
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
275-
return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal, requestedScopes);
276279
}
277280

278-
return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal);
281+
// @formatter:off
282+
Map<String, Object> additionalParameters = parameters
283+
.entrySet()
284+
.stream()
285+
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) &&
286+
!e.getKey().equals(OAuth2ParameterNames.REFRESH_TOKEN) &&
287+
!e.getKey().equals(OAuth2ParameterNames.SCOPE))
288+
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
289+
// @formatter:on
290+
291+
return new OAuth2RefreshTokenAuthenticationToken(
292+
refreshToken, clientPrincipal, requestedScopes, additionalParameters);
279293
}
280294
}
281295

@@ -299,13 +313,23 @@ public Authentication convert(HttpServletRequest request) {
299313
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
300314
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE);
301315
}
316+
Set<String> requestedScopes = null;
302317
if (StringUtils.hasText(scope)) {
303-
Set<String> requestedScopes = new HashSet<>(
318+
requestedScopes = new HashSet<>(
304319
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
305-
return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScopes);
306320
}
307321

308-
return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
322+
// @formatter:off
323+
Map<String, Object> additionalParameters = parameters
324+
.entrySet()
325+
.stream()
326+
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) &&
327+
!e.getKey().equals(OAuth2ParameterNames.SCOPE))
328+
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
329+
// @formatter:on
330+
331+
return new OAuth2ClientCredentialsAuthenticationToken(
332+
clientPrincipal, requestedScopes, additionalParameters);
309333
}
310334
}
311335
}

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThe
108108
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
109109
TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(
110110
registeredClient.getClientId(), registeredClient.getClientSecret());
111-
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
111+
OAuth2ClientCredentialsAuthenticationToken authentication =
112+
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
112113

113114
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
114115
.isInstanceOf(OAuth2AuthenticationException.class)
@@ -122,7 +123,8 @@ public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2Authen
122123
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
123124
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
124125
registeredClient.getClientId(), registeredClient.getClientSecret(), ClientAuthenticationMethod.BASIC, null);
125-
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
126+
OAuth2ClientCredentialsAuthenticationToken authentication =
127+
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
126128

127129
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
128130
.isInstanceOf(OAuth2AuthenticationException.class)
@@ -137,7 +139,8 @@ public void authenticateWhenClientNotAuthorizedToRequestTokenThenThrowOAuth2Auth
137139
.authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS))
138140
.build();
139141
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
140-
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
142+
OAuth2ClientCredentialsAuthenticationToken authentication =
143+
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
141144

142145
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
143146
.isInstanceOf(OAuth2AuthenticationException.class)
@@ -151,7 +154,7 @@ public void authenticateWhenInvalidScopeThenThrowOAuth2AuthenticationException()
151154
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
152155
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
153156
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(
154-
clientPrincipal, Collections.singleton("invalid-scope"));
157+
clientPrincipal, Collections.singleton("invalid-scope"), null);
155158

156159
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
157160
.isInstanceOf(OAuth2AuthenticationException.class)
@@ -166,7 +169,7 @@ public void authenticateWhenScopeRequestedThenAccessTokenContainsScope() {
166169
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
167170
Set<String> requestedScope = Collections.singleton("scope1");
168171
OAuth2ClientCredentialsAuthenticationToken authentication =
169-
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScope);
172+
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScope, null);
170173

171174
when(this.jwtEncoder.encode(any(), any()))
172175
.thenReturn(createJwt(Collections.singleton("mapped-scoped")));
@@ -180,7 +183,8 @@ public void authenticateWhenScopeRequestedThenAccessTokenContainsScope() {
180183
public void authenticateWhenValidAuthenticationThenReturnAccessToken() {
181184
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
182185
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
183-
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
186+
OAuth2ClientCredentialsAuthenticationToken authentication =
187+
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null);
184188

185189
when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt(registeredClient.getScopes()));
186190

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.security.oauth2.server.authorization.authentication;
1717

1818
import java.util.Collections;
19+
import java.util.Map;
1920
import java.util.Set;
2021

2122
import org.junit.Test;
@@ -34,42 +35,39 @@
3435
public class OAuth2ClientCredentialsAuthenticationTokenTests {
3536
private final OAuth2ClientAuthenticationToken clientPrincipal =
3637
new OAuth2ClientAuthenticationToken(TestRegisteredClients.registeredClient().build());
38+
private Set<String> scopes = Collections.singleton("scope1");
39+
private Map<String, Object> additionalParameters = Collections.singletonMap("param1", "value1");
3740

3841
@Test
3942
public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {
40-
assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null))
43+
assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null, this.scopes, this.additionalParameters))
4144
.isInstanceOf(IllegalArgumentException.class)
4245
.hasMessage("clientPrincipal cannot be null");
4346
}
4447

45-
@Test
46-
public void constructorWhenScopesNullThenThrowIllegalArgumentException() {
47-
assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(this.clientPrincipal, null))
48-
.isInstanceOf(IllegalArgumentException.class)
49-
.hasMessage("scopes cannot be null");
50-
}
51-
5248
@Test
5349
public void constructorWhenClientPrincipalProvidedThenCreated() {
54-
OAuth2ClientCredentialsAuthenticationToken authentication =
55-
new OAuth2ClientCredentialsAuthenticationToken(this.clientPrincipal);
50+
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(
51+
this.clientPrincipal, this.scopes, this.additionalParameters);
5652

5753
assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);
5854
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);
5955
assertThat(authentication.getCredentials().toString()).isEmpty();
60-
assertThat(authentication.getScopes()).isEmpty();
56+
assertThat(authentication.getScopes()).isEqualTo(this.scopes);
57+
assertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);
6158
}
6259

6360
@Test
6461
public void constructorWhenScopesProvidedThenCreated() {
6562
Set<String> expectedScopes = Collections.singleton("test-scope");
6663

67-
OAuth2ClientCredentialsAuthenticationToken authentication =
68-
new OAuth2ClientCredentialsAuthenticationToken(this.clientPrincipal, expectedScopes);
64+
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(
65+
this.clientPrincipal, expectedScopes, this.additionalParameters);
6966

7067
assertThat(authentication.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS);
7168
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);
7269
assertThat(authentication.getCredentials().toString()).isEmpty();
7370
assertThat(authentication.getScopes()).isEqualTo(expectedScopes);
71+
assertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters);
7472
}
7573
}

0 commit comments

Comments
 (0)