Skip to content

Commit 491da9d

Browse files
clementkngjzheaux
authored andcommitted
Added OAuth2TokenAttributes to wrap attributes
To simplify access to OAuth 2.0 token attributes Fixes gh-6498
1 parent ee8182d commit 491da9d

File tree

6 files changed

+92
-21
lines changed

6 files changed

+92
-21
lines changed

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import org.springframework.security.core.authority.SimpleGrantedAuthority;
8080
import org.springframework.security.core.userdetails.UserDetailsService;
8181
import org.springframework.security.oauth2.core.OAuth2Error;
82+
import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
8283
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
8384
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
8485
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
@@ -159,7 +160,7 @@ public class OAuth2ResourceServerConfigurerTests {
159160
private static final String CLIENT_ID = "client-id";
160161
private static final String CLIENT_SECRET = "client-secret";
161162
private static final OAuth2IntrospectionAuthenticationToken INTROSPECTION_AUTHENTICATION_TOKEN =
162-
new OAuth2IntrospectionAuthenticationToken(noScopes(), JWT_CLAIMS, Collections.emptyList());
163+
new OAuth2IntrospectionAuthenticationToken(noScopes(), new OAuth2TokenAttributes(JWT_CLAIMS), Collections.emptyList());
163164

164165
@Autowired(required = false)
165166
MockMvc mvc;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.core;
18+
19+
import java.util.Collections;
20+
import java.util.Map;
21+
22+
/**
23+
* A domain object that wraps the attributes of an OAuth 2.0 token.
24+
*
25+
* @author Clement Ng
26+
* @since 5.2
27+
*/
28+
public final class OAuth2TokenAttributes {
29+
private final Map<String, Object> attributes;
30+
31+
/**
32+
* Constructs an {@code OAuth2TokenAttributes} using the provided parameters.
33+
*
34+
* @param attributes the attributes of the OAuth 2.0 token
35+
*/
36+
public OAuth2TokenAttributes(Map<String, Object> attributes) {
37+
this.attributes = Collections.unmodifiableMap(attributes);
38+
}
39+
40+
/**
41+
* Gets the attributes of the OAuth 2.0 token in map form.
42+
*
43+
* @return a {@link Map} of the attribute's objects keyed by the attribute's names
44+
*/
45+
public Map<String, Object> getAttributes() {
46+
return attributes;
47+
}
48+
49+
/**
50+
* Gets the attribute of the OAuth 2.0 token corresponding to the name.
51+
*
52+
* @param name the name to lookup in the attributes
53+
* @return the object corresponding to the name in the attributes
54+
*/
55+
public <A> A getAttribute(String name) {
56+
return (A) this.attributes.get(name);
57+
}
58+
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.security.oauth2.core.OAuth2AccessToken;
3333
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
3434
import org.springframework.security.oauth2.core.OAuth2Error;
35+
import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
3536
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
3637
import org.springframework.security.oauth2.server.resource.introspection.OAuth2TokenIntrospectionClient;
3738
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
@@ -123,7 +124,7 @@ private AbstractAuthenticationToken convert(String token, Map<String, Object> cl
123124
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
124125
token, iat, exp);
125126
Collection<GrantedAuthority> authorities = extractAuthorities(claims);
126-
return new OAuth2IntrospectionAuthenticationToken(accessToken, claims, authorities);
127+
return new OAuth2IntrospectionAuthenticationToken(accessToken, new OAuth2TokenAttributes(claims), authorities);
127128
}
128129

129130
private Collection<GrantedAuthority> extractAuthorities(Map<String, Object> claims) {

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationToken.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.security.core.SpringSecurityCoreVersion;
2525
import org.springframework.security.core.Transient;
2626
import org.springframework.security.oauth2.core.OAuth2AccessToken;
27+
import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
2728
import org.springframework.util.Assert;
2829

2930
import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
@@ -53,7 +54,7 @@ public class OAuth2IntrospectionAuthenticationToken
5354
* @param authorities The authorities associated with the given token
5455
*/
5556
public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token,
56-
Map<String, Object> attributes, Collection<? extends GrantedAuthority> authorities) {
57+
OAuth2TokenAttributes attributes, Collection<? extends GrantedAuthority> authorities) {
5758

5859
this(token, attributes, authorities, null);
5960
}
@@ -65,18 +66,20 @@ public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token,
6566
* @param authorities The authorities associated with the given token
6667
* @param name The name associated with this token
6768
*/
68-
public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token,
69-
Map<String, Object> attributes, Collection<? extends GrantedAuthority> authorities, String name) {
69+
public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token, OAuth2TokenAttributes attributes,
70+
Collection<? extends GrantedAuthority> authorities, String name) {
7071

7172
super(token, attributes(attributes), token, authorities);
7273
this.attributes = attributes(attributes);
73-
this.name = name == null ? (String) attributes.get(SUBJECT) : name;
74+
this.name = name == null ? (String) this.attributes.get(SUBJECT) : name;
7475
setAuthenticated(true);
7576
}
7677

77-
private static Map<String, Object> attributes(Map<String, Object> attributes) {
78-
Assert.notEmpty(attributes, "attributes cannot be empty");
79-
return Collections.unmodifiableMap(new LinkedHashMap<>(attributes));
78+
private static Map<String, Object> attributes(OAuth2TokenAttributes attributes) {
79+
Assert.notNull(attributes, "attributes cannot be empty");
80+
Map<String, Object> attr = attributes.getAttributes();
81+
Assert.notEmpty(attr, "attributes cannot be empty");
82+
return Collections.unmodifiableMap(new LinkedHashMap<>(attr));
8083
}
8184

8285
/**

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionReactiveAuthenticationManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Optional;
2424
import java.util.stream.Collectors;
2525

26+
import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
2627
import reactor.core.publisher.Mono;
2728

2829
import org.springframework.http.HttpStatus;
@@ -101,7 +102,7 @@ private Mono<OAuth2IntrospectionAuthenticationToken> authenticate(String token)
101102
OAuth2AccessToken accessToken =
102103
new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
103104
Collection<GrantedAuthority> authorities = extractAuthorities(claims);
104-
return new OAuth2IntrospectionAuthenticationToken(accessToken, claims, authorities);
105+
return new OAuth2IntrospectionAuthenticationToken(accessToken, new OAuth2TokenAttributes(claims), authorities);
105106
})
106107
.onErrorMap(OAuth2IntrospectionException.class, this::onError);
107108
}

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationTokenTests.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.security.core.GrantedAuthority;
3131
import org.springframework.security.core.authority.AuthorityUtils;
3232
import org.springframework.security.oauth2.core.OAuth2AccessToken;
33+
import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
3334

3435
import static org.assertj.core.api.Assertions.assertThat;
3536
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -46,14 +47,15 @@ public class OAuth2IntrospectionAuthenticationTokenTests {
4647
private final OAuth2AccessToken token =
4748
new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
4849
"token", Instant.now(), Instant.now().plusSeconds(3600));
49-
private final Map<String, Object> attributes = new HashMap<>();
5050
private final String name = "sub";
51+
private Map<String, Object> attributesMap = new HashMap<>();
52+
private final OAuth2TokenAttributes attributes = new OAuth2TokenAttributes(attributesMap);
5153

5254
@Before
5355
public void setUp() {
54-
this.attributes.put(SUBJECT, this.name);
55-
this.attributes.put(CLIENT_ID, "client_id");
56-
this.attributes.put(USERNAME, "username");
56+
this.attributesMap.put(SUBJECT, this.name);
57+
this.attributesMap.put(CLIENT_ID, "client_id");
58+
this.attributesMap.put(USERNAME, "username");
5759
}
5860

5961
@Test
@@ -67,7 +69,8 @@ public void getNameWhenConfiguredInConstructorThenReturnsName() {
6769
@Test
6870
public void getNameWhenHasNoSubjectThenReturnsNull() {
6971
OAuth2IntrospectionAuthenticationToken authenticated =
70-
new OAuth2IntrospectionAuthenticationToken(this.token, Collections.singletonMap("claim", "value"),
72+
new OAuth2IntrospectionAuthenticationToken(this.token,
73+
new OAuth2TokenAttributes(Collections.singletonMap("claim", "value")),
7174
Collections.emptyList());
7275
assertThat(authenticated.getName()).isNull();
7376
}
@@ -76,7 +79,7 @@ public void getNameWhenHasNoSubjectThenReturnsNull() {
7679
public void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {
7780
OAuth2IntrospectionAuthenticationToken authenticated =
7881
new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes, Collections.emptyList());
79-
assertThat(authenticated.getName()).isEqualTo(this.attributes.get(SUBJECT));
82+
assertThat(authenticated.getName()).isEqualTo(this.attributes.getAttribute(SUBJECT));
8083
}
8184

8285
@Test
@@ -92,15 +95,17 @@ public void constructorWhenAttributesAreNullOrEmptyThenThrowsException() {
9295
.isInstanceOf(IllegalArgumentException.class)
9396
.hasMessageContaining("attributes cannot be empty");
9497

95-
assertThatCode(() -> new OAuth2IntrospectionAuthenticationToken(this.token, Collections.emptyMap(), null))
98+
assertThatCode(() -> new OAuth2IntrospectionAuthenticationToken(this.token,
99+
new OAuth2TokenAttributes(Collections.emptyMap()), null))
96100
.isInstanceOf(IllegalArgumentException.class)
97101
.hasMessageContaining("attributes cannot be empty");
98102
}
99103

100104
@Test
101105
public void constructorWhenPassingAllAttributesThenTokenIsAuthenticated() {
102106
OAuth2IntrospectionAuthenticationToken authenticated =
103-
new OAuth2IntrospectionAuthenticationToken(this.token, Collections.singletonMap("claim", "value"),
107+
new OAuth2IntrospectionAuthenticationToken(this.token,
108+
new OAuth2TokenAttributes(Collections.singletonMap("claim", "value")),
104109
Collections.emptyList(), "harris");
105110
assertThat(authenticated.isAuthenticated()).isTrue();
106111
}
@@ -109,7 +114,7 @@ public void constructorWhenPassingAllAttributesThenTokenIsAuthenticated() {
109114
public void getTokenAttributesWhenHasTokenThenReturnsThem() {
110115
OAuth2IntrospectionAuthenticationToken authenticated =
111116
new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes, Collections.emptyList());
112-
assertThat(authenticated.getTokenAttributes()).isEqualTo(this.attributes);
117+
assertThat(authenticated.getTokenAttributes()).isEqualTo(this.attributes.getAttributes());
113118
}
114119

115120
@Test
@@ -126,7 +131,8 @@ public void constructorWhenDefaultParametersThenSetsPrincipalToAttributesCopy()
126131
JSONObject attributes = new JSONObject();
127132
attributes.put("active", true);
128133
OAuth2IntrospectionAuthenticationToken token =
129-
new OAuth2IntrospectionAuthenticationToken(this.token, attributes, Collections.emptyList());
134+
new OAuth2IntrospectionAuthenticationToken(this.token, new OAuth2TokenAttributes(attributes),
135+
Collections.emptyList());
130136
assertThat(token.getPrincipal()).isNotSameAs(attributes);
131137
assertThat(token.getTokenAttributes()).isNotSameAs(attributes);
132138
}
@@ -136,7 +142,8 @@ public void constructorWhenDefaultParametersThenSetsPrincipalToAttributesCopy()
136142
public void toStringWhenAttributesContainsURLThenDoesNotFail() throws Exception {
137143
JSONObject attributes = new JSONObject(Collections.singletonMap("iss", new URL("https://idp.example.com")));
138144
OAuth2IntrospectionAuthenticationToken token =
139-
new OAuth2IntrospectionAuthenticationToken(this.token, attributes, Collections.emptyList());
145+
new OAuth2IntrospectionAuthenticationToken(this.token, new OAuth2TokenAttributes(attributes),
146+
Collections.emptyList());
140147
assertThatCode(token::toString)
141148
.doesNotThrowAnyException();
142149
}

0 commit comments

Comments
 (0)