Skip to content

Commit 7f29585

Browse files
committed
Remove OidcUserService.setAccessibleScopes()
Closes gh-18056
1 parent 2eb5da3 commit 7f29585

5 files changed

Lines changed: 25 additions & 145 deletions

File tree

config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@
6060
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
6161
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses;
6262
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
63+
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
6364
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
65+
import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers;
6466
import org.springframework.security.oauth2.core.user.OAuth2User;
6567
import org.springframework.security.oauth2.core.user.TestOAuth2Users;
6668
import org.springframework.security.oauth2.jwt.Jwt;
@@ -133,6 +135,9 @@ public class OAuth2LoginBeanDefinitionParserTests {
133135
@Autowired(required = false)
134136
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService;
135137

138+
@Autowired(required = false)
139+
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oidcUserService;
140+
136141
@Autowired(required = false)
137142
private JwtDecoderFactory<ClientRegistration> jwtDecoderFactory;
138143

@@ -286,6 +291,8 @@ public void requestWhenOidcAuthenticationResponseValidThenJwtDecoderFactoryCalle
286291
given(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);
287292
Jwt jwt = TestJwts.user();
288293
given(this.jwtDecoderFactory.createDecoder(any())).willReturn((token) -> jwt);
294+
DefaultOidcUser oidcUser = TestOidcUsers.create();
295+
given(this.oidcUserService.loadUser(any())).willReturn(oidcUser);
289296
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
290297
params.add("code", "code123");
291298
params.add("state", authorizationRequest.getState());
@@ -339,6 +346,8 @@ public void requestWhenCustomGrantedAuthoritiesMapperThenCalled() throws Excepti
339346
given(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);
340347
Jwt jwt = TestJwts.user();
341348
given(this.jwtDecoderFactory.createDecoder(any())).willReturn((token) -> jwt);
349+
DefaultOidcUser oidcUser = TestOidcUsers.create();
350+
given(this.oidcUserService.loadUser(any())).willReturn(oidcUser);
342351
given(this.userAuthoritiesMapper.mapAuthorities(any()))
343352
.willReturn((Collection) AuthorityUtils.createAuthorityList("ROLE_OIDC_USER"));
344353
// @formatter:off

config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<intercept-url pattern="/**" access="authenticated"/>
2929
<oauth2-login access-token-response-client-ref="accessTokenResponseClient"
3030
user-service-ref="oauth2UserService"
31+
oidc-user-service-ref="oidcUserService"
3132
user-authorities-mapper-ref="userAuthoritiesMapper"
3233
jwt-decoder-factory-ref="jwtDecoderFactory"
3334
authorization-request-repository-ref="authorizationRequestRepository"
@@ -39,6 +40,9 @@
3940
</b:bean>
4041
<b:bean id="oauth2UserService" class="org.mockito.Mockito" factory-method="mock">
4142
<b:constructor-arg value="org.springframework.security.oauth2.client.userinfo.OAuth2UserService" type="java.lang.Class"/>
43+
</b:bean>
44+
<b:bean id="oidcUserService" class="org.mockito.Mockito" factory-method="mock">
45+
<b:constructor-arg value="org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService" type="java.lang.Class"/>
4246
</b:bean>
4347
<b:bean id="userAuthoritiesMapper" class="org.mockito.Mockito" factory-method="mock">
4448
<b:constructor-arg value="org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper" type="java.lang.Class"/>

config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@
2727
<http auto-config="true">
2828
<intercept-url pattern="/**" access="authenticated"/>
2929
<oauth2-login access-token-response-client-ref="accessTokenResponseClient"
30+
oidc-user-service-ref="oidcUserService"
3031
jwt-decoder-factory-ref="jwtDecoderFactory"
3132
authorization-request-repository-ref="authorizationRequestRepository"/>
3233
<request-cache ref="requestCache" />
3334
</http>
3435

3536
<b:bean id="accessTokenResponseClient" class="org.mockito.Mockito" factory-method="mock">
3637
<b:constructor-arg value="org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient" type="java.lang.Class"/>
38+
</b:bean>
39+
<b:bean id="oidcUserService" class="org.mockito.Mockito" factory-method="mock">
40+
<b:constructor-arg value="org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService" type="java.lang.Class"/>
3741
</b:bean>
3842
<b:bean id="jwtDecoderFactory" class="org.mockito.Mockito" factory-method="mock">
3943
<b:constructor-arg value="org.springframework.security.oauth2.jwt.JwtDecoderFactory" type="java.lang.Class"/>

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java

Lines changed: 7 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717
package org.springframework.security.oauth2.client.oidc.userinfo;
1818

1919
import java.time.Instant;
20-
import java.util.Arrays;
2120
import java.util.HashMap;
22-
import java.util.HashSet;
2321
import java.util.Map;
24-
import java.util.Set;
2522
import java.util.function.BiFunction;
2623
import java.util.function.Function;
2724
import java.util.function.Predicate;
@@ -41,15 +38,13 @@
4138
import org.springframework.security.oauth2.core.converter.ClaimConversionService;
4239
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter;
4340
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
44-
import org.springframework.security.oauth2.core.oidc.OidcScopes;
4541
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
4642
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
4743
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
4844
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
4945
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
5046
import org.springframework.security.oauth2.core.user.OAuth2User;
5147
import org.springframework.util.Assert;
52-
import org.springframework.util.CollectionUtils;
5348
import org.springframework.util.StringUtils;
5449

5550
/**
@@ -72,9 +67,6 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
7267
private static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER = new ClaimTypeConverter(
7368
createDefaultClaimTypeConverters());
7469

75-
private Set<String> accessibleScopes = new HashSet<>(
76-
Arrays.asList(OidcScopes.PROFILE, OidcScopes.EMAIL, OidcScopes.ADDRESS, OidcScopes.PHONE));
77-
7870
private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService = new DefaultOAuth2UserService();
7971

8072
private Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory = (
@@ -150,30 +142,10 @@ private Map<String, Object> getClaims(OidcUserRequest userRequest, OAuth2User oa
150142
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
151143
// Auto-disabled if UserInfo Endpoint URI is not provided
152144
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
153-
if (!StringUtils.hasLength(providerDetails.getUserInfoEndpoint().getUri())) {
154-
return false;
155-
}
156-
// The Claims requested by the profile, email, address, and phone scope values
157-
// are returned from the UserInfo Endpoint (as described in Section 5.3.2),
158-
// when a response_type value is used that results in an Access Token being
159-
// issued.
160-
// However, when no Access Token is issued, which is the case for the
161-
// response_type=id_token,
162-
// the resulting Claims are returned in the ID Token.
163-
// The Authorization Code Grant Flow, which is response_type=code, results in an
164-
// Access Token being issued.
165-
if (AuthorizationGrantType.AUTHORIZATION_CODE
166-
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
167-
// Return true if there is at least one match between the authorized scope(s)
168-
// and accessible scope(s)
169-
//
170-
// Also return true if authorized scope(s) is empty, because the provider has
171-
// not indicated which scopes are accessible via the access token
172-
// @formatter:off
173-
return this.accessibleScopes.isEmpty()
174-
|| CollectionUtils.isEmpty(userRequest.getAccessToken().getScopes())
175-
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
176-
// @formatter:on
145+
if (StringUtils.hasLength(providerDetails.getUserInfoEndpoint().getUri())
146+
&& AuthorizationGrantType.AUTHORIZATION_CODE
147+
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
148+
return true;
177149
}
178150
return false;
179151
}
@@ -204,40 +176,18 @@ public final void setClaimTypeConverterFactory(
204176
this.claimTypeConverterFactory = claimTypeConverterFactory;
205177
}
206178

207-
/**
208-
* Sets the scope(s) that allow access to the user info resource. The default is
209-
* {@link OidcScopes#PROFILE profile}, {@link OidcScopes#EMAIL email},
210-
* {@link OidcScopes#ADDRESS address} and {@link OidcScopes#PHONE phone}. The scope(s)
211-
* are checked against the "granted" scope(s) associated to the
212-
* {@link OidcUserRequest#getAccessToken() access token} to determine if the user info
213-
* resource is accessible or not. If there is at least one match, the user info
214-
* resource will be requested, otherwise it will not.
215-
* @param accessibleScopes the scope(s) that allow access to the user info resource
216-
* @since 5.2
217-
* @deprecated Use {@link #setRetrieveUserInfo(Predicate)} instead
218-
*/
219-
@Deprecated(since = "6.3", forRemoval = true)
220-
public final void setAccessibleScopes(Set<String> accessibleScopes) {
221-
Assert.notNull(accessibleScopes, "accessibleScopes cannot be null");
222-
this.accessibleScopes = accessibleScopes;
223-
}
224-
225179
/**
226180
* Sets the {@code Predicate} used to determine if the UserInfo Endpoint should be
227181
* called to retrieve information about the End-User (Resource Owner).
228182
* <p>
229-
* By default, the UserInfo Endpoint is called if all of the following are true:
183+
* By default, the UserInfo Endpoint is called if all the following are true:
230184
* <ul>
231185
* <li>The user info endpoint is defined on the ClientRegistration</li>
232186
* <li>The Client Registration uses the
233187
* {@link AuthorizationGrantType#AUTHORIZATION_CODE}</li>
234-
* <li>The access token contains one or more scopes allowed to access the UserInfo
235-
* Endpoint ({@link OidcScopes#PROFILE profile}, {@link OidcScopes#EMAIL email},
236-
* {@link OidcScopes#ADDRESS address} or {@link OidcScopes#PHONE phone}) or the access
237-
* token scopes are empty</li>
238188
* </ul>
239-
* @param retrieveUserInfo the function used to determine if the UserInfo Endpoint
240-
* should be called
189+
* @param retrieveUserInfo the {@code Predicate} used to determine if the UserInfo
190+
* Endpoint should be called
241191
* @since 6.3
242192
*/
243193
public final void setRetrieveUserInfo(Predicate<OidcUserRequest> retrieveUserInfo) {

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java

Lines changed: 1 addition & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.springframework.security.oauth2.client.oidc.userinfo;
1818

1919
import java.time.Instant;
20-
import java.util.Collections;
2120
import java.util.HashMap;
2221
import java.util.Iterator;
2322
import java.util.Map;
@@ -130,16 +129,6 @@ public void setClaimTypeConverterFactoryWhenNullThenThrowIllegalArgumentExceptio
130129
assertThatIllegalArgumentException().isThrownBy(() -> this.userService.setClaimTypeConverterFactory(null));
131130
}
132131

133-
@Test
134-
public void setAccessibleScopesWhenNullThenThrowIllegalArgumentException() {
135-
assertThatIllegalArgumentException().isThrownBy(() -> this.userService.setAccessibleScopes(null));
136-
}
137-
138-
@Test
139-
public void setAccessibleScopesWhenEmptyThenSet() {
140-
this.userService.setAccessibleScopes(Collections.emptySet());
141-
}
142-
143132
@Test
144133
public void setRetrieveUserInfoWhenNullThenThrowIllegalArgumentException() {
145134
// @formatter:off
@@ -179,83 +168,6 @@ public void loadUserWhenUserInfoUriIsNullThenUserInfoEndpointNotRequested() {
179168
assertThat(user.getUserInfo()).isNull();
180169
}
181170

182-
@Test
183-
public void loadUserWhenNonStandardScopesAuthorizedThenUserInfoEndpointNotRequested() {
184-
ClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri("https://provider.com/user")
185-
.build();
186-
this.accessToken = TestOAuth2AccessTokens.scopes("scope1", "scope2");
187-
OidcUser user = this.userService
188-
.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));
189-
assertThat(user.getUserInfo()).isNull();
190-
}
191-
192-
// gh-6886
193-
@Test
194-
public void loadUserWhenNonStandardScopesAuthorizedAndAccessibleScopesMatchThenUserInfoEndpointRequested() {
195-
// @formatter:off
196-
String userInfoResponse = "{\n"
197-
+ " \"sub\": \"subject1\",\n"
198-
+ " \"name\": \"first last\",\n"
199-
+ " \"given_name\": \"first\",\n"
200-
+ " \"family_name\": \"last\",\n"
201-
+ " \"preferred_username\": \"user1\",\n"
202-
+ " \"email\": \"user1@example.com\"\n"
203-
+ "}\n";
204-
// @formatter:on
205-
this.server.enqueue(jsonResponse(userInfoResponse));
206-
String userInfoUri = this.server.url("/user").toString();
207-
ClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();
208-
this.accessToken = TestOAuth2AccessTokens.scopes("scope1", "scope2");
209-
this.userService.setAccessibleScopes(Collections.singleton("scope2"));
210-
OidcUser user = this.userService
211-
.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));
212-
assertThat(user.getUserInfo()).isNotNull();
213-
}
214-
215-
// gh-6886
216-
@Test
217-
public void loadUserWhenNonStandardScopesAuthorizedAndAccessibleScopesEmptyThenUserInfoEndpointRequested() {
218-
// @formatter:off
219-
String userInfoResponse = "{\n"
220-
+ " \"sub\": \"subject1\",\n"
221-
+ " \"name\": \"first last\",\n"
222-
+ " \"given_name\": \"first\",\n"
223-
+ " \"family_name\": \"last\",\n"
224-
+ " \"preferred_username\": \"user1\",\n"
225-
+ " \"email\": \"user1@example.com\"\n"
226-
+ "}\n";
227-
// @formatter:on
228-
this.server.enqueue(jsonResponse(userInfoResponse));
229-
String userInfoUri = this.server.url("/user").toString();
230-
ClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();
231-
this.accessToken = TestOAuth2AccessTokens.scopes("scope1", "scope2");
232-
this.userService.setAccessibleScopes(Collections.emptySet());
233-
OidcUser user = this.userService
234-
.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));
235-
assertThat(user.getUserInfo()).isNotNull();
236-
}
237-
238-
// gh-6886
239-
@Test
240-
public void loadUserWhenStandardScopesAuthorizedThenUserInfoEndpointRequested() {
241-
// @formatter:off
242-
String userInfoResponse = "{\n"
243-
+ " \"sub\": \"subject1\",\n"
244-
+ " \"name\": \"first last\",\n"
245-
+ " \"given_name\": \"first\",\n"
246-
+ " \"family_name\": \"last\",\n"
247-
+ " \"preferred_username\": \"user1\",\n"
248-
+ " \"email\": \"user1@example.com\"\n"
249-
+ "}\n";
250-
// @formatter:on
251-
this.server.enqueue(jsonResponse(userInfoResponse));
252-
String userInfoUri = this.server.url("/user").toString();
253-
ClientRegistration clientRegistration = this.clientRegistrationBuilder.userInfoUri(userInfoUri).build();
254-
OidcUser user = this.userService
255-
.loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken));
256-
assertThat(user.getUserInfo()).isNotNull();
257-
}
258-
259171
@Test
260172
public void loadUserWhenCustomRetrieveUserInfoSetThenUsed() {
261173
// @formatter:off
@@ -571,6 +483,7 @@ public void loadUserWhenCustomClaimTypeConverterFactorySetThenApplied() {
571483
@Test
572484
public void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {
573485
OidcUserService userService = new OidcUserService();
486+
userService.setRetrieveUserInfo((req) -> false);
574487
OidcUserRequest request = new OidcUserRequest(TestClientRegistrations.clientRegistration().build(),
575488
TestOAuth2AccessTokens.scopes("message:read", "message:write"), TestOidcIdTokens.idToken().build());
576489
OidcUser user = userService.loadUser(request);

0 commit comments

Comments
 (0)