Skip to content

Commit 99aee99

Browse files
filiphrsjohnr
authored andcommitted
Expose user name attribute name in OAuth2UserAuthority
1 parent b41ec0a commit 99aee99

File tree

13 files changed

+92
-13
lines changed

13 files changed

+92
-13
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,18 @@ static boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
7878

7979
static OidcUser getUser(OidcUserRequest userRequest, OidcUserInfo userInfo) {
8080
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
81-
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
81+
ClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
82+
String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
83+
if (StringUtils.hasLength(userNameAttributeName)) {
84+
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo, userNameAttributeName));
85+
}
86+
else {
87+
authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
88+
}
8289
OAuth2AccessToken token = userRequest.getAccessToken();
8390
for (String scope : token.getScopes()) {
8491
authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
8592
}
86-
ClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
87-
String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
8893
if (StringUtils.hasText(userNameAttributeName)) {
8994
return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName);
9095
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
9595
ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);
9696
OAuth2AccessToken token = userRequest.getAccessToken();
9797
Map<String, Object> attributes = this.attributesConverter.convert(userRequest).convert(response.getBody());
98-
Collection<GrantedAuthority> authorities = getAuthorities(token, attributes);
98+
Collection<GrantedAuthority> authorities = getAuthorities(token, attributes, userNameAttributeName);
9999
return new DefaultOAuth2User(authorities, attributes, userNameAttributeName);
100100
}
101101

@@ -187,9 +187,10 @@ private String getUserNameAttributeName(OAuth2UserRequest userRequest) {
187187
return userNameAttributeName;
188188
}
189189

190-
private Collection<GrantedAuthority> getAuthorities(OAuth2AccessToken token, Map<String, Object> attributes) {
190+
private Collection<GrantedAuthority> getAuthorities(OAuth2AccessToken token, Map<String, Object> attributes,
191+
String userNameAttributeName) {
191192
Collection<GrantedAuthority> authorities = new LinkedHashSet<>();
192-
authorities.add(new OAuth2UserAuthority(attributes));
193+
authorities.add(new OAuth2UserAuthority(attributes, userNameAttributeName));
193194
for (String authority : token.getScopes()) {
194195
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
195196
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2Aut
130130
.bodyToMono(DefaultReactiveOAuth2UserService.STRING_OBJECT_MAP)
131131
.mapNotNull((attributes) -> this.attributesConverter.convert(userRequest).convert(attributes));
132132
return userAttributes.map((attrs) -> {
133-
GrantedAuthority authority = new OAuth2UserAuthority(attrs);
133+
GrantedAuthority authority = new OAuth2UserAuthority(attrs, userNameAttributeName);
134134
Set<GrantedAuthority> authorities = new HashSet<>();
135135
authorities.add(authority);
136136
OAuth2AccessToken token = userRequest.getAccessToken();

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ private static String asJson(OAuth2UserAuthority oauth2UserAuthority) {
247247
return "{\n" +
248248
" \"@class\": \"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\",\n" +
249249
" \"authority\": \"" + oauth2UserAuthority.getAuthority() + "\",\n" +
250+
" \"userNameAttributeName\": \"username\",\n" +
250251
" \"attributes\": {\n" +
251252
" \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
252253
" \"username\": \"user\"\n" +
@@ -260,6 +261,7 @@ private static String asJson(OidcUserAuthority oidcUserAuthority) {
260261
return "{\n" +
261262
" \"@class\": \"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\",\n" +
262263
" \"authority\": \"" + oidcUserAuthority.getAuthority() + "\",\n" +
264+
" \"userNameAttributeName\": \"" + oidcUserAuthority.getUserNameAttributeName() + "\",\n" +
263265
" \"idToken\": " + asJson(oidcUserAuthority.getIdToken()) + ",\n" +
264266
" \"userInfo\": " + asJson(oidcUserAuthority.getUserInfo()) + "\n" +
265267
" }";

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ public void loadUserWhenTokenDoesNotContainScopesThenNoScopeAuthorities() {
313313
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
314314
assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
315315
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
316+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("id");
316317
}
317318

318319
@Test
@@ -361,6 +362,7 @@ public void loadUserWhenNestedUserInfoSuccessThenReturnUser() throws IOException
361362
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
362363
assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
363364
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
365+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
364366
}
365367
}
366368

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@ public void loadUserWhenNestedUserInfoSuccessThenReturnUser() {
616616
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
617617
assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
618618
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
619+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
619620
}
620621

621622
private MockResponse jsonResponse(String json) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ public void loadUserWhenUserInfoSuccessResponseThenReturnUser() {
156156
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
157157
assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
158158
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
159+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
159160
}
160161

161162
@Test
@@ -196,6 +197,7 @@ public void loadUserWhenNestedUserInfoSuccessThenReturnUser() {
196197
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
197198
assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
198199
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
200+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
199201
}
200202

201203
@Test

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ public void loadUserWhenUserInfoSuccessResponseThenReturnUser() {
144144
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
145145
assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
146146
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
147+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("id");
147148
}
148149

149150
// gh-9336
@@ -203,6 +204,7 @@ public void loadUserWhenNestedUserInfoSuccessThenReturnUser() {
203204
OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
204205
assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
205206
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
207+
assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
206208
}
207209

208210
// gh-5500

oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Map;
2121

2222
import org.springframework.security.core.GrantedAuthority;
23+
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
2324
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
2425
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
2526
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
@@ -57,6 +58,19 @@ public OidcUserAuthority(OidcIdToken idToken, OidcUserInfo userInfo) {
5758
this("OIDC_USER", idToken, userInfo);
5859
}
5960

61+
/**
62+
* Constructs a {@code OidcUserAuthority} using the provided parameters and defaults
63+
* {@link #getAuthority()} to {@code OIDC_USER}.
64+
* @param idToken the {@link OidcIdToken ID Token} containing claims about the user
65+
* @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,
66+
* may be {@code null}
67+
* @param userNameAttributeName the attribute name used to access the user's name from
68+
* the attributes
69+
*/
70+
public OidcUserAuthority(OidcIdToken idToken, OidcUserInfo userInfo, String userNameAttributeName) {
71+
this("OIDC_USER", idToken, userInfo, userNameAttributeName);
72+
}
73+
6074
/**
6175
* Constructs a {@code OidcUserAuthority} using the provided parameters.
6276
* @param authority the authority granted to the user
@@ -65,7 +79,21 @@ public OidcUserAuthority(OidcIdToken idToken, OidcUserInfo userInfo) {
6579
* may be {@code null}
6680
*/
6781
public OidcUserAuthority(String authority, OidcIdToken idToken, OidcUserInfo userInfo) {
68-
super(authority, collectClaims(idToken, userInfo));
82+
this(authority, idToken, userInfo, IdTokenClaimNames.SUB);
83+
}
84+
85+
/**
86+
* Constructs a {@code OidcUserAuthority} using the provided parameters.
87+
* @param authority the authority granted to the user
88+
* @param idToken the {@link OidcIdToken ID Token} containing claims about the user
89+
* @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,
90+
* may be {@code null}
91+
* @param userNameAttributeName the attribute name used to access the user's name from
92+
* the attributes
93+
*/
94+
public OidcUserAuthority(String authority, OidcIdToken idToken, OidcUserInfo userInfo,
95+
String userNameAttributeName) {
96+
super(authority, collectClaims(idToken, userInfo), userNameAttributeName);
6997
this.idToken = idToken;
7098
this.userInfo = userInfo;
7199
}

oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthority.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323
import java.util.Objects;
2424

25+
import org.springframework.lang.Nullable;
2526
import org.springframework.security.core.GrantedAuthority;
2627
import org.springframework.security.core.SpringSecurityCoreVersion;
2728
import org.springframework.util.Assert;
@@ -41,6 +42,8 @@ public class OAuth2UserAuthority implements GrantedAuthority {
4142

4243
private final Map<String, Object> attributes;
4344

45+
private final String userNameAttributeName;
46+
4447
/**
4548
* Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults
4649
* {@link #getAuthority()} to {@code OAUTH2_USER}.
@@ -50,16 +53,39 @@ public OAuth2UserAuthority(Map<String, Object> attributes) {
5053
this("OAUTH2_USER", attributes);
5154
}
5255

56+
/**
57+
* Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults
58+
* {@link #getAuthority()} to {@code OAUTH2_USER}.
59+
* @param attributes the attributes about the user
60+
* @param userNameAttributeName the attribute name used to access the user's name from
61+
* the attributes
62+
*/
63+
public OAuth2UserAuthority(Map<String, Object> attributes, @Nullable String userNameAttributeName) {
64+
this("OAUTH2_USER", attributes, userNameAttributeName);
65+
}
66+
5367
/**
5468
* Constructs a {@code OAuth2UserAuthority} using the provided parameters.
5569
* @param authority the authority granted to the user
5670
* @param attributes the attributes about the user
5771
*/
5872
public OAuth2UserAuthority(String authority, Map<String, Object> attributes) {
73+
this(authority, attributes, null);
74+
}
75+
76+
/**
77+
* Constructs a {@code OAuth2UserAuthority} using the provided parameters.
78+
* @param authority the authority granted to the user
79+
* @param attributes the attributes about the user
80+
* @param userNameAttributeName the attribute name used to access the user's name from
81+
* the attributes
82+
*/
83+
public OAuth2UserAuthority(String authority, Map<String, Object> attributes, String userNameAttributeName) {
5984
Assert.hasText(authority, "authority cannot be empty");
6085
Assert.notEmpty(attributes, "attributes cannot be empty");
6186
this.authority = authority;
6287
this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));
88+
this.userNameAttributeName = userNameAttributeName;
6389
}
6490

6591
@Override
@@ -75,6 +101,15 @@ public Map<String, Object> getAttributes() {
75101
return this.attributes;
76102
}
77103

104+
/**
105+
* Returns the attribute name used to access the user's name from the attributes.
106+
* @return the attribute name used to access the user's name from the attributes
107+
*/
108+
@Nullable
109+
public String getUserNameAttributeName() {
110+
return this.userNameAttributeName;
111+
}
112+
78113
@Override
79114
public boolean equals(Object obj) {
80115
if (this == obj) {

oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/TestOAuth2Users.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ public static DefaultOAuth2User create() {
3737
String nameAttributeKey = "username";
3838
Map<String, Object> attributes = new HashMap<>();
3939
attributes.put(nameAttributeKey, "user");
40-
Collection<GrantedAuthority> authorities = authorities(attributes);
40+
Collection<GrantedAuthority> authorities = authorities(attributes, nameAttributeKey);
4141
return new DefaultOAuth2User(authorities, attributes, nameAttributeKey);
4242
}
4343

44-
private static Collection<GrantedAuthority> authorities(Map<String, Object> attributes) {
45-
return new LinkedHashSet<>(Arrays.asList(new OAuth2UserAuthority(attributes),
44+
private static Collection<GrantedAuthority> authorities(Map<String, Object> attributes,
45+
String userNameAttributeName) {
46+
return new LinkedHashSet<>(Arrays.asList(new OAuth2UserAuthority(attributes, userNameAttributeName),
4647
new SimpleGrantedAuthority("SCOPE_read"), new SimpleGrantedAuthority("SCOPE_write")));
4748
}
4849

test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ private ClientRegistration.Builder clientRegistrationBuilder() {
834834

835835
private Collection<GrantedAuthority> defaultAuthorities() {
836836
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
837-
authorities.add(new OAuth2UserAuthority(this.attributes.get()));
837+
authorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));
838838
for (String authority : this.accessToken.getScopes()) {
839839
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
840840
}

test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,7 @@ private ClientRegistration.Builder clientRegistrationBuilder() {
13761376

13771377
private Collection<GrantedAuthority> defaultAuthorities() {
13781378
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
1379-
authorities.add(new OAuth2UserAuthority(this.attributes.get()));
1379+
authorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));
13801380
for (String authority : this.accessToken.getScopes()) {
13811381
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
13821382
}

0 commit comments

Comments
 (0)