Skip to content

Commit 17064fc

Browse files
committed
Merge branch '6.3.x'
2 parents 58747d2 + dd5edeb commit 17064fc

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospector.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -183,7 +183,7 @@ private Map<String, Object> adaptToNimbusResponse(ResponseEntity<Map<String, Obj
183183
return claims;
184184
}
185185

186-
private OAuth2TokenIntrospectionClaimAccessor convertClaimsSet(Map<String, Object> claims) {
186+
private ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {
187187
Map<String, Object> converted = new LinkedHashMap<>(claims);
188188
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {
189189
if (v instanceof String) {
@@ -277,4 +277,18 @@ private static final class ArrayListFromString extends ArrayList<String> {
277277

278278
}
279279

280+
// gh-15165
281+
private interface ArrayListFromStringClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {
282+
283+
@Override
284+
default List<String> getScopes() {
285+
Object value = getClaims().get(OAuth2TokenIntrospectionClaimNames.SCOPE);
286+
if (value instanceof ArrayListFromString list) {
287+
return list;
288+
}
289+
return OAuth2TokenIntrospectionClaimAccessor.super.getScopes();
290+
}
291+
292+
}
293+
280294
}

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospector.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -143,7 +143,7 @@ private Mono<Map<String, Object>> adaptToNimbusResponse(ClientResponse responseE
143143
.switchIfEmpty(Mono.error(() -> new BadOpaqueTokenException("Provided token isn't active")));
144144
}
145145

146-
private OAuth2TokenIntrospectionClaimAccessor convertClaimsSet(Map<String, Object> claims) {
146+
private ArrayListFromStringClaimAccessor convertClaimsSet(Map<String, Object> claims) {
147147
Map<String, Object> converted = new LinkedHashMap<>(claims);
148148
converted.computeIfPresent(OAuth2TokenIntrospectionClaimNames.AUD, (k, v) -> {
149149
if (v instanceof String) {
@@ -231,4 +231,18 @@ private static final class ArrayListFromString extends ArrayList<String> {
231231

232232
}
233233

234+
// gh-15165
235+
private interface ArrayListFromStringClaimAccessor extends OAuth2TokenIntrospectionClaimAccessor {
236+
237+
@Override
238+
default List<String> getScopes() {
239+
Object value = getClaims().get(OAuth2TokenIntrospectionClaimNames.SCOPE);
240+
if (value instanceof ArrayListFromString list) {
241+
return list;
242+
}
243+
return OAuth2TokenIntrospectionClaimAccessor.super.getScopes();
244+
}
245+
246+
}
247+
234248
}

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringOpaqueTokenIntrospectorTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
import org.springframework.http.MediaType;
4040
import org.springframework.http.RequestEntity;
4141
import org.springframework.http.ResponseEntity;
42+
import org.springframework.security.core.authority.AuthorityUtils;
4243
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
4344
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;
4445
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
@@ -245,6 +246,21 @@ public void introspectWhenIntrospectionTokenReturnsMalformedScopeThenEmptyAuthor
245246
assertThat(scope).containsExactly("read", "write", "dolphin");
246247
}
247248

249+
// gh-15165
250+
@Test
251+
public void introspectWhenActiveThenMapsAuthorities() {
252+
RestOperations restOperations = mock(RestOperations.class);
253+
OpaqueTokenIntrospector introspectionClient = new SpringOpaqueTokenIntrospector(INTROSPECTION_URL,
254+
restOperations);
255+
given(restOperations.exchange(any(RequestEntity.class), eq(STRING_OBJECT_MAP))).willReturn(ACTIVE);
256+
OAuth2AuthenticatedPrincipal principal = introspectionClient.introspect("token");
257+
assertThat(principal.getAuthorities()).isNotEmpty();
258+
Collection<String> scope = principal.getAttribute("scope");
259+
assertThat(scope).containsExactly("read", "write", "dolphin");
260+
Collection<String> authorities = AuthorityUtils.authorityListToSet(principal.getAuthorities());
261+
assertThat(authorities).containsExactly("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
262+
}
263+
248264
@Test
249265
public void constructorWhenIntrospectionUriIsNullThenIllegalArgumentException() {
250266
assertThatIllegalArgumentException()

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import java.time.Instant;
2121
import java.util.Arrays;
2222
import java.util.Base64;
23+
import java.util.Collection;
2324
import java.util.HashMap;
2425
import java.util.Map;
2526
import java.util.Optional;
@@ -37,6 +38,7 @@
3738
import org.springframework.http.HttpHeaders;
3839
import org.springframework.http.HttpStatus;
3940
import org.springframework.http.MediaType;
41+
import org.springframework.security.core.authority.AuthorityUtils;
4042
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
4143
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimAccessor;
4244
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
@@ -197,6 +199,20 @@ public void authenticateWhenIntrospectionTokenReturnsInvalidResponseThenInvalidT
197199
// @formatter:on
198200
}
199201

202+
// gh-15165
203+
@Test
204+
public void introspectWhenActiveThenMapsAuthorities() {
205+
WebClient webClient = mockResponse(ACTIVE_RESPONSE);
206+
SpringReactiveOpaqueTokenIntrospector introspectionClient = new SpringReactiveOpaqueTokenIntrospector(
207+
INTROSPECTION_URL, webClient);
208+
OAuth2AuthenticatedPrincipal principal = introspectionClient.introspect("token").block();
209+
assertThat(principal.getAuthorities()).isNotEmpty();
210+
Collection<String> scope = principal.getAttribute("scope");
211+
assertThat(scope).containsExactly("read", "write", "dolphin");
212+
Collection<String> authorities = AuthorityUtils.authorityListToSet(principal.getAuthorities());
213+
assertThat(authorities).containsExactly("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
214+
}
215+
200216
@Test
201217
public void setAuthenticationConverterWhenConverterIsNullThenExceptionIsThrown() {
202218
WebClient web = mock(WebClient.class);

0 commit comments

Comments
 (0)