Skip to content

Commit a916115

Browse files
committed
Add Authorities from Access Token
1 parent b846898 commit a916115

File tree

8 files changed

+167
-136
lines changed

8 files changed

+167
-136
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
import org.springframework.core.convert.TypeDescriptor;
1919
import org.springframework.core.convert.converter.Converter;
2020
import org.springframework.security.core.GrantedAuthority;
21+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2122
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2223
import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;
2324
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
2425
import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
26+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
2527
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
2628
import org.springframework.security.oauth2.core.OAuth2Error;
2729
import org.springframework.security.oauth2.core.converter.ClaimConversionService;
@@ -99,6 +101,10 @@ public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws OAuth2Authent
99101
OidcUserInfo userInfo = authority.getUserInfo();
100102
Set<GrantedAuthority> authorities = new HashSet<>();
101103
authorities.add(authority);
104+
OAuth2AccessToken token = userRequest.getAccessToken();
105+
for (String scope : token.getScopes()) {
106+
authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
107+
}
102108
String userNameAttributeName = userRequest.getClientRegistration()
103109
.getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
104110
if (StringUtils.hasText(userNameAttributeName)) {

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import java.time.Instant;
1919
import java.util.Arrays;
20-
import java.util.Collection;
21-
import java.util.Collections;
2220
import java.util.HashMap;
2321
import java.util.HashSet;
2422
import java.util.LinkedHashSet;
@@ -29,11 +27,13 @@
2927
import org.springframework.core.convert.TypeDescriptor;
3028
import org.springframework.core.convert.converter.Converter;
3129
import org.springframework.security.core.GrantedAuthority;
30+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
3231
import org.springframework.security.oauth2.client.registration.ClientRegistration;
3332
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
3433
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
3534
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
3635
import org.springframework.security.oauth2.core.AuthorizationGrantType;
36+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3737
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3838
import org.springframework.security.oauth2.core.OAuth2Error;
3939
import org.springframework.security.oauth2.core.converter.ClaimConversionService;
@@ -96,7 +96,6 @@ public class OidcUserService implements OAuth2UserService<OidcUserRequest, OidcU
9696
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
9797
Assert.notNull(userRequest, "userRequest cannot be null");
9898
OidcUserInfo userInfo = null;
99-
Collection<? extends GrantedAuthority> oauth2UserAuthorities = Collections.emptyList();
10099
if (this.shouldRetrieveUserInfo(userRequest)) {
101100
OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest);
102101

@@ -109,7 +108,6 @@ public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2Authenticatio
109108
claims = DEFAULT_CLAIM_TYPE_CONVERTER.convert(oauth2User.getAttributes());
110109
}
111110
userInfo = new OidcUserInfo(claims);
112-
oauth2UserAuthorities = oauth2User.getAuthorities();
113111

114112
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
115113

@@ -133,7 +131,10 @@ public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2Authenticatio
133131

134132
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
135133
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
136-
authorities.addAll(oauth2UserAuthorities);
134+
OAuth2AccessToken token = userRequest.getAccessToken();
135+
for (String authority : token.getScopes()) {
136+
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
137+
}
137138

138139
OidcUser user;
139140

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

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
*/
1616
package org.springframework.security.oauth2.client.userinfo;
1717

18-
import java.util.Arrays;
19-
import java.util.Collection;
20-
import java.util.Collections;
2118
import java.util.LinkedHashSet;
2219
import java.util.Map;
2320
import java.util.Set;
@@ -30,7 +27,7 @@
3027
import org.springframework.security.core.authority.SimpleGrantedAuthority;
3128
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
3229
import org.springframework.security.oauth2.client.registration.ClientRegistration;
33-
import org.springframework.security.oauth2.core.ClaimAccessor;
30+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3431
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3532
import org.springframework.security.oauth2.core.OAuth2AuthorizationException;
3633
import org.springframework.security.oauth2.core.OAuth2Error;
@@ -71,9 +68,6 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
7168
private static final ParameterizedTypeReference<Map<String, Object>> PARAMETERIZED_RESPONSE_TYPE =
7269
new ParameterizedTypeReference<Map<String, Object>>() {};
7370

74-
private static final Collection<String> WELL_KNOWN_AUTHORITIES_CLAIM_NAMES =
75-
Arrays.asList("scope", "scp");
76-
7771
private Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter = new OAuth2UserRequestEntityConverter();
7872

7973
private RestOperations restOperations;
@@ -137,7 +131,8 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
137131
Map<String, Object> userAttributes = response.getBody();
138132
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
139133
authorities.add(new OAuth2UserAuthority(userAttributes));
140-
for (String authority : getAuthorities(() -> userAttributes)) {
134+
OAuth2AccessToken token = userRequest.getAccessToken();
135+
for (String authority : token.getScopes()) {
141136
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
142137
}
143138

@@ -172,34 +167,4 @@ public final void setRestOperations(RestOperations restOperations) {
172167
Assert.notNull(restOperations, "restOperations cannot be null");
173168
this.restOperations = restOperations;
174169
}
175-
176-
private String getAuthoritiesClaimName(ClaimAccessor claims) {
177-
for (String claimName : WELL_KNOWN_AUTHORITIES_CLAIM_NAMES) {
178-
if (claims.containsClaim(claimName)) {
179-
return claimName;
180-
}
181-
}
182-
return null;
183-
}
184-
185-
private Collection<String> getAuthorities(ClaimAccessor claims) {
186-
String claimName = getAuthoritiesClaimName(claims);
187-
188-
if (claimName == null) {
189-
return Collections.emptyList();
190-
}
191-
192-
Object authorities = claims.getClaim(claimName);
193-
if (authorities instanceof String) {
194-
if (StringUtils.hasText((String) authorities)) {
195-
return Arrays.asList(((String) authorities).split(" "));
196-
} else {
197-
return Collections.emptyList();
198-
}
199-
} else if (authorities instanceof Collection) {
200-
return (Collection<String>) authorities;
201-
}
202-
203-
return Collections.emptyList();
204-
}
205170
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import org.springframework.http.MediaType;
2929
import org.springframework.security.authentication.AuthenticationServiceException;
3030
import org.springframework.security.core.GrantedAuthority;
31+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
3132
import org.springframework.security.oauth2.core.AuthenticationMethod;
33+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3234
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3335
import org.springframework.security.oauth2.core.OAuth2Error;
3436
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
@@ -131,6 +133,10 @@ public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest)
131133
GrantedAuthority authority = new OAuth2UserAuthority(attrs);
132134
Set<GrantedAuthority> authorities = new HashSet<>();
133135
authorities.add(authority);
136+
OAuth2AccessToken token = userRequest.getAccessToken();
137+
for (String scope : token.getScopes()) {
138+
authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
139+
}
134140

135141
return new DefaultOAuth2User(authorities, attrs, userNameAttributeName);
136142
})

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

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@
1616

1717
package org.springframework.security.oauth2.client.oidc.userinfo;
1818

19+
import java.time.Duration;
20+
import java.time.Instant;
21+
import java.util.Collections;
22+
import java.util.HashMap;
23+
import java.util.Iterator;
24+
import java.util.Map;
25+
import java.util.function.Function;
26+
1927
import org.junit.Before;
2028
import org.junit.Test;
2129
import org.junit.runner.RunWith;
2230
import org.mockito.Mock;
2331
import org.mockito.junit.MockitoJUnitRunner;
32+
import reactor.core.publisher.Mono;
33+
2434
import org.springframework.core.convert.converter.Converter;
35+
import org.springframework.security.core.GrantedAuthority;
2536
import org.springframework.security.core.authority.AuthorityUtils;
37+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2638
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2739
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
2840
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
@@ -36,17 +48,20 @@
3648
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
3749
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
3850
import org.springframework.security.oauth2.core.user.OAuth2User;
39-
import reactor.core.publisher.Mono;
40-
41-
import java.time.Duration;
42-
import java.time.Instant;
43-
import java.util.Collections;
44-
import java.util.HashMap;
45-
import java.util.Map;
46-
import java.util.function.Function;
47-
48-
import static org.assertj.core.api.Assertions.*;
49-
import static org.mockito.Mockito.*;
51+
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
52+
53+
import static org.assertj.core.api.Assertions.assertThat;
54+
import static org.assertj.core.api.Assertions.assertThatCode;
55+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
56+
import static org.mockito.Mockito.any;
57+
import static org.mockito.Mockito.mock;
58+
import static org.mockito.Mockito.same;
59+
import static org.mockito.Mockito.verify;
60+
import static org.mockito.Mockito.when;
61+
import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientRegistration;
62+
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
63+
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.scopes;
64+
import static org.springframework.security.oauth2.core.oidc.TestOidcIdTokens.idToken;
5065

5166
/**
5267
* @author Rob Winch
@@ -178,6 +193,38 @@ public void loadUserWhenCustomClaimTypeConverterFactorySetThenApplied() {
178193
verify(customClaimTypeConverterFactory).apply(same(userRequest.getClientRegistration()));
179194
}
180195

196+
@Test
197+
public void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {
198+
Map<String, Object> body = new HashMap<>();
199+
body.put("id", "id");
200+
body.put("sub", "test-subject");
201+
OidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();
202+
OidcUserRequest request = new OidcUserRequest(
203+
clientRegistration().build(), scopes("message:read", "message:write"), idToken(body));
204+
OidcUser user = userService.loadUser(request).block();
205+
206+
assertThat(user.getAuthorities()).hasSize(3);
207+
Iterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();
208+
assertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);
209+
assertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority("SCOPE_message:read"));
210+
assertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority("SCOPE_message:write"));
211+
}
212+
213+
@Test
214+
public void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {
215+
Map<String, Object> body = new HashMap<>();
216+
body.put("id", "id");
217+
body.put("sub", "test-subject");
218+
OidcReactiveOAuth2UserService userService = new OidcReactiveOAuth2UserService();
219+
OidcUserRequest request = new OidcUserRequest(
220+
clientRegistration().build(), noScopes(), idToken(body));
221+
OidcUser user = userService.loadUser(request).block();
222+
223+
assertThat(user.getAuthorities()).hasSize(1);
224+
Iterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();
225+
assertThat(authorities.next()).isInstanceOf(OAuth2UserAuthority.class);
226+
}
227+
181228
private OidcUserRequest userRequest() {
182229
return new OidcUserRequest(this.registration.build(), this.accessToken, this.idToken);
183230
}

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

Lines changed: 8 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.springframework.security.oauth2.client.oidc.userinfo;
1717

1818
import java.time.Instant;
19-
import java.util.Arrays;
2019
import java.util.Collections;
2120
import java.util.HashMap;
2221
import java.util.Iterator;
@@ -33,19 +32,14 @@
3332
import org.junit.Test;
3433
import org.junit.rules.ExpectedException;
3534

36-
import org.springframework.core.ParameterizedTypeReference;
3735
import org.springframework.core.convert.converter.Converter;
3836
import org.springframework.http.HttpHeaders;
3937
import org.springframework.http.HttpMethod;
40-
import org.springframework.http.HttpStatus;
4138
import org.springframework.http.MediaType;
42-
import org.springframework.http.RequestEntity;
43-
import org.springframework.http.ResponseEntity;
4439
import org.springframework.security.core.GrantedAuthority;
4540
import org.springframework.security.core.authority.SimpleGrantedAuthority;
4641
import org.springframework.security.oauth2.client.registration.ClientRegistration;
4742
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
48-
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
4943
import org.springframework.security.oauth2.core.AuthenticationMethod;
5044
import org.springframework.security.oauth2.core.OAuth2AccessToken;
5145
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
@@ -56,18 +50,16 @@
5650
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
5751
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
5852
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
59-
import org.springframework.web.client.RestOperations;
6053

6154
import static org.assertj.core.api.Assertions.assertThat;
6255
import static org.assertj.core.api.Assertions.assertThatThrownBy;
6356
import static org.hamcrest.CoreMatchers.containsString;
64-
import static org.mockito.Mockito.any;
6557
import static org.mockito.Mockito.mock;
66-
import static org.mockito.Mockito.nullable;
6758
import static org.mockito.Mockito.same;
6859
import static org.mockito.Mockito.verify;
6960
import static org.mockito.Mockito.when;
7061
import static org.springframework.security.oauth2.client.registration.TestClientRegistrations.clientRegistration;
62+
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.noScopes;
7163
import static org.springframework.security.oauth2.core.TestOAuth2AccessTokens.scopes;
7264
import static org.springframework.security.oauth2.core.oidc.TestOidcIdTokens.idToken;
7365

@@ -272,7 +264,7 @@ public void loadUserWhenUserInfoSuccessResponseThenReturnUser() {
272264
assertThat(user.getUserInfo().getPreferredUsername()).isEqualTo("user1");
273265
assertThat(user.getUserInfo().getEmail()).isEqualTo("[email protected]");
274266

275-
assertThat(user.getAuthorities().size()).isEqualTo(1);
267+
assertThat(user.getAuthorities().size()).isEqualTo(3);
276268
assertThat(user.getAuthorities().iterator().next()).isInstanceOf(OidcUserAuthority.class);
277269
OidcUserAuthority userAuthority = (OidcUserAuthority) user.getAuthorities().iterator().next();
278270
assertThat(userAuthority.getAuthority()).isEqualTo("ROLE_USER");
@@ -499,15 +491,13 @@ public void loadUserWhenCustomClaimTypeConverterFactorySetThenApplied() {
499491
}
500492

501493
@Test
502-
public void loadUserWhenAttributesContainScopeThenIndividualScopeAuthorities() {
494+
public void loadUserWhenTokenContainsScopesThenIndividualScopeAuthorities() {
503495
Map<String, Object> body = new HashMap<>();
504496
body.put("id", "id");
505497
body.put("sub", "test-subject");
506-
body.put("scope", "message:read message:write");
507498
OidcUserService userService = new OidcUserService();
508-
userService.setOauth2UserService(withMockResponse(body));
509-
OidcUserRequest request = new OidcUserRequest(clientRegistration().
510-
userInfoUri("uri").build(), scopes("profile"), idToken(body));
499+
OidcUserRequest request = new OidcUserRequest(clientRegistration().build(),
500+
scopes("message:read", "message:write"), idToken(body));
511501
OidcUser user = userService.loadUser(request);
512502

513503
assertThat(user.getAuthorities()).hasSize(3);
@@ -518,53 +508,20 @@ public void loadUserWhenAttributesContainScopeThenIndividualScopeAuthorities() {
518508
}
519509

520510
@Test
521-
public void loadUserWhenAttributesContainScpThenIndividualScopeAuthorities() {
511+
public void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {
522512
Map<String, Object> body = new HashMap<>();
523513
body.put("id", "id");
524514
body.put("sub", "test-subject");
525-
body.put("scp", Arrays.asList("message:read", "message:write"));
526515
OidcUserService userService = new OidcUserService();
527-
userService.setOauth2UserService(withMockResponse(body));
528-
OidcUserRequest request = new OidcUserRequest(clientRegistration().
529-
userInfoUri("uri").build(), scopes("profile"), idToken(body));
530-
OidcUser user = userService.loadUser(request);
531-
532-
assertThat(user.getAuthorities()).hasSize(3);
533-
Iterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();
534-
assertThat(authorities.next()).isInstanceOf(OidcUserAuthority.class);
535-
assertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority("SCOPE_message:read"));
536-
assertThat(authorities.next()).isEqualTo(new SimpleGrantedAuthority("SCOPE_message:write"));
537-
}
538-
539-
@Test
540-
public void loadUserWhenAttributesDoesNotContainScopesThenNoScopeAuthorities() {
541-
Map<String, Object> body = new HashMap<>();
542-
body.put("id", "id");
543-
body.put("sub", "test-subject");
544-
body.put("authorities", Arrays.asList("message:read", "message:write"));
545-
OidcUserService userService = new OidcUserService();
546-
userService.setOauth2UserService(withMockResponse(body));
547-
OidcUserRequest request = new OidcUserRequest(clientRegistration().
548-
userInfoUri("uri").build(), scopes("profile"), idToken(body));
516+
OidcUserRequest request = new OidcUserRequest(clientRegistration().build(),
517+
noScopes(), idToken(body));
549518
OidcUser user = userService.loadUser(request);
550519

551520
assertThat(user.getAuthorities()).hasSize(1);
552521
Iterator<? extends GrantedAuthority> authorities = user.getAuthorities().iterator();
553522
assertThat(authorities.next()).isInstanceOf(OidcUserAuthority.class);
554523
}
555524

556-
private DefaultOAuth2UserService withMockResponse(Map<String, Object> response) {
557-
ResponseEntity<Map<String, Object>> responseEntity = new ResponseEntity<>(response, HttpStatus.OK);
558-
Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter = mock(Converter.class);
559-
RestOperations rest = mock(RestOperations.class);
560-
when(rest.exchange(nullable(RequestEntity.class), any(ParameterizedTypeReference.class)))
561-
.thenReturn(responseEntity);
562-
DefaultOAuth2UserService userService = new DefaultOAuth2UserService();
563-
userService.setRequestEntityConverter(requestEntityConverter);
564-
userService.setRestOperations(rest);
565-
return userService;
566-
}
567-
568525
private MockResponse jsonResponse(String json) {
569526
return new MockResponse()
570527
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)

0 commit comments

Comments
 (0)