Browse Source

Polish Opaque Token

Use OAuth2AuthenticatedPrincipal
Use BearerTokenAuthentication
Update names to reflect more generic approach.

Fixes gh-7344
Fixes gh-7345
Josh Cummings 6 years ago
parent
commit
068f4f0147
19 changed files with 222 additions and 520 deletions
  1. 3 3
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java
  2. 2 2
      config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java
  3. 5 4
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java
  4. 0 58
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenAttributes.java
  5. 0 100
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationToken.java
  6. 18 34
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java
  7. 15 32
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManager.java
  8. 20 6
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java
  9. 20 6
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java
  10. 3 1
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OpaqueTokenIntrospector.java
  11. 3 1
      oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/ReactiveOpaqueTokenIntrospector.java
  12. 69 0
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/core/TestOAuth2AuthenticatedPrincipals.java
  13. 0 150
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationTokenTests.java
  14. 23 27
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProviderTests.java
  15. 28 26
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManagerTests.java
  16. 5 4
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospectorTests.java
  17. 5 4
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java
  18. 0 59
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/TestOAuth2TokenIntrospectionClientResponses.java
  19. 3 3
      samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerController.java

+ 3 - 3
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java

@@ -37,7 +37,7 @@ import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
-import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationProvider;
+import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
@@ -388,8 +388,8 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
 			}
 			}
 
 
 			OpaqueTokenIntrospector introspector = getIntrospector();
 			OpaqueTokenIntrospector introspector = getIntrospector();
-			OAuth2IntrospectionAuthenticationProvider provider =
-					new OAuth2IntrospectionAuthenticationProvider(introspector);
+			OpaqueTokenAuthenticationProvider provider =
+					new OpaqueTokenAuthenticationProvider(introspector);
 			http.authenticationProvider(provider);
 			http.authenticationProvider(provider);
 
 
 			return http.getSharedObject(AuthenticationManager.class);
 			return http.getSharedObject(AuthenticationManager.class);

+ 2 - 2
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -87,7 +87,7 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
 import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
 import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
 import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
-import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionReactiveAuthenticationManager;
+import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;
 import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
 import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
@@ -1867,7 +1867,7 @@ public class ServerHttpSecurity {
 			}
 			}
 
 
 			protected ReactiveAuthenticationManager getAuthenticationManager() {
 			protected ReactiveAuthenticationManager getAuthenticationManager() {
-				return new OAuth2IntrospectionReactiveAuthenticationManager(getIntrospector());
+				return new OpaqueTokenReactiveAuthenticationManager(getIntrospector());
 			}
 			}
 
 
 			protected ReactiveOpaqueTokenIntrospector getIntrospector() {
 			protected ReactiveOpaqueTokenIntrospector getIntrospector() {

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

@@ -78,8 +78,8 @@ import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.OAuth2Error;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidator;
 import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
 import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
 import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
@@ -91,7 +91,7 @@ import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
-import org.springframework.security.oauth2.server.resource.authentication.OAuth2IntrospectionAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
 import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
@@ -159,8 +159,9 @@ public class OAuth2ResourceServerConfigurerTests {
 	private static final String INTROSPECTION_URI = "https://idp.example.com";
 	private static final String INTROSPECTION_URI = "https://idp.example.com";
 	private static final String CLIENT_ID = "client-id";
 	private static final String CLIENT_ID = "client-id";
 	private static final String CLIENT_SECRET = "client-secret";
 	private static final String CLIENT_SECRET = "client-secret";
-	private static final OAuth2IntrospectionAuthenticationToken INTROSPECTION_AUTHENTICATION_TOKEN =
-			new OAuth2IntrospectionAuthenticationToken(noScopes(), new OAuth2TokenAttributes(JWT_CLAIMS), Collections.emptyList());
+	private static final BearerTokenAuthentication INTROSPECTION_AUTHENTICATION_TOKEN =
+			new BearerTokenAuthentication(new DefaultOAuth2AuthenticatedPrincipal(JWT_CLAIMS, Collections.emptyList()),
+					noScopes(), Collections.emptyList());
 
 
 	@Autowired(required = false)
 	@Autowired(required = false)
 	MockMvc mvc;
 	MockMvc mvc;

+ 0 - 58
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/OAuth2TokenAttributes.java

@@ -1,58 +0,0 @@
-/*
- * Copyright 2002-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.oauth2.core;
-
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * A domain object that wraps the attributes of an OAuth 2.0 token.
- *
- * @author Clement Ng
- * @since 5.2
- */
-public final class OAuth2TokenAttributes {
-	private final Map<String, Object> attributes;
-
-	/**
-	 * Constructs an {@code OAuth2TokenAttributes} using the provided parameters.
-	 *
-	 * @param attributes the attributes of the OAuth 2.0 token
-	 */
-	public OAuth2TokenAttributes(Map<String, Object> attributes) {
-		this.attributes = Collections.unmodifiableMap(attributes);
-	}
-
-	/**
-	 * Gets the attributes of the OAuth 2.0 token in map form.
-	 *
-	 * @return a {@link Map} of the attribute's objects keyed by the attribute's names
-	 */
-	public Map<String, Object> getAttributes() {
-		return attributes;
-	}
-
-	/**
-	 * Gets the attribute of the OAuth 2.0 token corresponding to the name.
-	 *
-	 * @param name the name to lookup in the attributes
-	 * @return the object corresponding to the name in the attributes
-	 */
-	public <A> A getAttribute(String name) {
-		return (A) this.attributes.get(name);
-	}
-}

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

@@ -1,100 +0,0 @@
-/*
- * Copyright 2002-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.security.oauth2.server.resource.authentication;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.SpringSecurityCoreVersion;
-import org.springframework.security.core.Transient;
-import org.springframework.security.oauth2.core.OAuth2AccessToken;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
-import org.springframework.util.Assert;
-
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
-
-/**
- * An {@link org.springframework.security.core.Authentication} token that represents a successful authentication as
- * obtained through an opaque token
- * <a target="_blank" href="https://tools.ietf.org/html/rfc7662">introspection</a>
- * process.
- *
- * @author Josh Cummings
- * @since 5.2
- */
-@Transient
-public class OAuth2IntrospectionAuthenticationToken
-		extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken> {
-
-	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
-
-	private Map<String, Object> attributes;
-	private String name;
-
-	/**
-	 * Constructs a {@link OAuth2IntrospectionAuthenticationToken} with the provided arguments
-	 *
-	 * @param token The verified token
-	 * @param authorities The authorities associated with the given token
-	 */
-	public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token,
-			OAuth2TokenAttributes attributes, Collection<? extends GrantedAuthority> authorities) {
-
-		this(token, attributes, authorities, null);
-	}
-
-	/**
-	 * Constructs a {@link OAuth2IntrospectionAuthenticationToken} with the provided arguments
-	 *
-	 * @param token The verified token
-	 * @param authorities The authorities associated with the given token
-	 * @param name The name associated with this token
-	 */
-	public OAuth2IntrospectionAuthenticationToken(OAuth2AccessToken token, OAuth2TokenAttributes attributes,
-		Collection<? extends GrantedAuthority> authorities, String name) {
-
-		super(token, attributes, token, authorities);
-		this.attributes = attributes(attributes);
-		this.name = name == null ? (String) this.attributes.get(SUBJECT) : name;
-		setAuthenticated(true);
-	}
-
-	private static Map<String, Object> attributes(OAuth2TokenAttributes attributes) {
-		Assert.notNull(attributes, "attributes cannot be empty");
-		Map<String, Object> attr = attributes.getAttributes();
-		Assert.notEmpty(attr, "attributes cannot be empty");
-		return Collections.unmodifiableMap(new LinkedHashMap<>(attr));
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public Map<String, Object> getTokenAttributes() {
-		return this.attributes;
-	}
-
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public String getName() {
-		return this.name;
-	}
-}

+ 18 - 34
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationProvider.java → oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java

@@ -16,11 +16,7 @@
 package org.springframework.security.oauth2.server.resource.authentication;
 package org.springframework.security.oauth2.server.resource.authentication;
 
 
 import java.time.Instant;
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 
 
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
@@ -28,20 +24,18 @@ import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.OAuth2Error;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
-import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
-import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.BearerTokenError;
 import org.springframework.security.oauth2.server.resource.BearerTokenError;
+import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
+import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 
 
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUED_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUED_AT;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 
 
 /**
 /**
  * An {@link AuthenticationProvider} implementation for opaque
  * An {@link AuthenticationProvider} implementation for opaque
@@ -65,20 +59,20 @@ import static org.springframework.security.oauth2.server.resource.introspection.
  * @since 5.2
  * @since 5.2
  * @see AuthenticationProvider
  * @see AuthenticationProvider
  */
  */
-public final class OAuth2IntrospectionAuthenticationProvider implements AuthenticationProvider {
+public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
 	private static final BearerTokenError DEFAULT_INVALID_TOKEN =
 	private static final BearerTokenError DEFAULT_INVALID_TOKEN =
 			invalidToken("An error occurred while attempting to introspect the token: Invalid token");
 			invalidToken("An error occurred while attempting to introspect the token: Invalid token");
 
 
-	private OpaqueTokenIntrospector introspectionClient;
+	private OpaqueTokenIntrospector introspector;
 
 
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionAuthenticationProvider} with the provided parameters
+	 * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters
 	 *
 	 *
-	 * @param introspectionClient The {@link OpaqueTokenIntrospector} to use
+	 * @param introspector The {@link OpaqueTokenIntrospector} to use
 	 */
 	 */
-	public OAuth2IntrospectionAuthenticationProvider(OpaqueTokenIntrospector introspectionClient) {
-		Assert.notNull(introspectionClient, "introspectionClient cannot be null");
-		this.introspectionClient = introspectionClient;
+	public OpaqueTokenAuthenticationProvider(OpaqueTokenIntrospector introspector) {
+		Assert.notNull(introspector, "introspector cannot be null");
+		this.introspector = introspector;
 	}
 	}
 
 
 	/**
 	/**
@@ -97,15 +91,15 @@ public final class OAuth2IntrospectionAuthenticationProvider implements Authenti
 		}
 		}
 		BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
 		BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
 
 
-		Map<String, Object> claims;
+		OAuth2AuthenticatedPrincipal principal;
 		try {
 		try {
-			claims = this.introspectionClient.introspect(bearer.getToken());
+			principal = this.introspector.introspect(bearer.getToken());
 		} catch (OAuth2IntrospectionException failed) {
 		} catch (OAuth2IntrospectionException failed) {
 			OAuth2Error invalidToken = invalidToken(failed.getMessage());
 			OAuth2Error invalidToken = invalidToken(failed.getMessage());
 			throw new OAuth2AuthenticationException(invalidToken);
 			throw new OAuth2AuthenticationException(invalidToken);
 		}
 		}
 
 
-		AbstractAuthenticationToken result = convert(bearer.getToken(), claims);
+		AbstractAuthenticationToken result = convert(principal, bearer.getToken());
 		result.setDetails(bearer.getDetails());
 		result.setDetails(bearer.getDetails());
 		return result;
 		return result;
 	}
 	}
@@ -118,22 +112,12 @@ public final class OAuth2IntrospectionAuthenticationProvider implements Authenti
 		return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);
 		return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);
 	}
 	}
 
 
-	private AbstractAuthenticationToken convert(String token, Map<String, Object> claims) {
-		Instant iat = (Instant) claims.get(ISSUED_AT);
-		Instant exp = (Instant) claims.get(EXPIRES_AT);
-		OAuth2AccessToken accessToken  = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
+	private AbstractAuthenticationToken convert(OAuth2AuthenticatedPrincipal principal, String token) {
+		Instant iat = principal.getAttribute(ISSUED_AT);
+		Instant exp = principal.getAttribute(EXPIRES_AT);
+		OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
 				token, iat, exp);
 				token, iat, exp);
-		Collection<GrantedAuthority> authorities = extractAuthorities(claims);
-		return new OAuth2IntrospectionAuthenticationToken(accessToken, new OAuth2TokenAttributes(claims), authorities);
-	}
-
-	private Collection<GrantedAuthority> extractAuthorities(Map<String, Object> claims) {
-		Collection<String> scopes = (Collection<String>) claims.getOrDefault(SCOPE, Collections.emptyList());
-		List<GrantedAuthority> authorities = new ArrayList<>();
-		for (String scope : scopes) {
-			authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
-		}
-		return authorities;
+		return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
 	}
 	}
 
 
 	private static BearerTokenError invalidToken(String message) {
 	private static BearerTokenError invalidToken(String message) {

+ 15 - 32
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionReactiveAuthenticationManager.java → oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManager.java

@@ -17,32 +17,25 @@
 package org.springframework.security.oauth2.server.resource.authentication;
 package org.springframework.security.oauth2.server.resource.authentication;
 
 
 import java.time.Instant;
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
 
 
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
 import reactor.core.publisher.Mono;
 import reactor.core.publisher.Mono;
 
 
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
 import org.springframework.security.oauth2.core.OAuth2AccessToken;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2Error;
 import org.springframework.security.oauth2.core.OAuth2Error;
-import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
-import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.BearerTokenError;
 import org.springframework.security.oauth2.server.resource.BearerTokenError;
+import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
+import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 
 
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUED_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUED_AT;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 
 
 /**
 /**
  * An {@link ReactiveAuthenticationManager} implementation for opaque
  * An {@link ReactiveAuthenticationManager} implementation for opaque
@@ -66,20 +59,20 @@ import static org.springframework.security.oauth2.server.resource.introspection.
  * @since 5.2
  * @since 5.2
  * @see ReactiveAuthenticationManager
  * @see ReactiveAuthenticationManager
  */
  */
-public class OAuth2IntrospectionReactiveAuthenticationManager implements ReactiveAuthenticationManager {
+public class OpaqueTokenReactiveAuthenticationManager implements ReactiveAuthenticationManager {
 	private static final BearerTokenError DEFAULT_INVALID_TOKEN =
 	private static final BearerTokenError DEFAULT_INVALID_TOKEN =
 			invalidToken("An error occurred while attempting to introspect the token: Invalid token");
 			invalidToken("An error occurred while attempting to introspect the token: Invalid token");
 
 
-	private ReactiveOpaqueTokenIntrospector introspectionClient;
+	private ReactiveOpaqueTokenIntrospector introspector;
 
 
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionReactiveAuthenticationManager} with the provided parameters
+	 * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided parameters
 	 *
 	 *
-	 * @param introspectionClient The {@link ReactiveOpaqueTokenIntrospector} to use
+	 * @param introspector The {@link ReactiveOpaqueTokenIntrospector} to use
 	 */
 	 */
-	public OAuth2IntrospectionReactiveAuthenticationManager(ReactiveOpaqueTokenIntrospector introspectionClient) {
-		Assert.notNull(introspectionClient, "introspectionClient cannot be null");
-		this.introspectionClient = introspectionClient;
+	public OpaqueTokenReactiveAuthenticationManager(ReactiveOpaqueTokenIntrospector introspector) {
+		Assert.notNull(introspector, "introspector cannot be null");
+		this.introspector = introspector;
 	}
 	}
 
 
 	@Override
 	@Override
@@ -92,30 +85,20 @@ public class OAuth2IntrospectionReactiveAuthenticationManager implements Reactiv
 				.cast(Authentication.class);
 				.cast(Authentication.class);
 	}
 	}
 
 
-	private Mono<OAuth2IntrospectionAuthenticationToken> authenticate(String token) {
-		return this.introspectionClient.introspect(token)
-				.map(claims -> {
-					Instant iat = (Instant) claims.get(ISSUED_AT);
-					Instant exp = (Instant) claims.get(EXPIRES_AT);
+	private Mono<BearerTokenAuthentication> authenticate(String token) {
+		return this.introspector.introspect(token)
+				.map(principal -> {
+					Instant iat = principal.getAttribute(ISSUED_AT);
+					Instant exp = principal.getAttribute(EXPIRES_AT);
 
 
 					// construct token
 					// construct token
 					OAuth2AccessToken accessToken =
 					OAuth2AccessToken accessToken =
 							new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
 							new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
-					Collection<GrantedAuthority> authorities = extractAuthorities(claims);
-					return new OAuth2IntrospectionAuthenticationToken(accessToken, new OAuth2TokenAttributes(claims), authorities);
+					return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
 				})
 				})
 				.onErrorMap(OAuth2IntrospectionException.class, this::onError);
 				.onErrorMap(OAuth2IntrospectionException.class, this::onError);
 	}
 	}
 
 
-	private Collection<GrantedAuthority> extractAuthorities(Map<String, Object> claims) {
-		Collection<String> scopes = (Collection<String>) claims.getOrDefault(SCOPE, Collections.emptyList());
-		List<GrantedAuthority> authorities = new ArrayList<>();
-		for (String scope : scopes) {
-			authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
-		}
-		return authorities;
-	}
-
 	private static BearerTokenError invalidToken(String message) {
 	private static BearerTokenError invalidToken(String message) {
 		try {
 		try {
 			return new BearerTokenError("invalid_token",
 			return new BearerTokenError("invalid_token",

+ 20 - 6
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java

@@ -19,6 +19,8 @@ package org.springframework.security.oauth2.server.resource.introspection;
 import java.net.URI;
 import java.net.URI;
 import java.net.URL;
 import java.net.URL;
 import java.time.Instant;
 import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -36,6 +38,10 @@ import org.springframework.http.MediaType;
 import org.springframework.http.RequestEntity;
 import org.springframework.http.RequestEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.client.support.BasicAuthenticationInterceptor;
 import org.springframework.http.client.support.BasicAuthenticationInterceptor;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.MultiValueMap;
@@ -63,8 +69,10 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
 	private Converter<String, RequestEntity<?>> requestEntityConverter;
 	private Converter<String, RequestEntity<?>> requestEntityConverter;
 	private RestOperations restOperations;
 	private RestOperations restOperations;
 
 
+	private final String authorityPrefix = "SCOPE_";
+
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionAuthenticationProvider} with the provided parameters
+	 * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters
 	 *
 	 *
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param clientId The client id authorized to introspect
 	 * @param clientId The client id authorized to introspect
@@ -82,7 +90,7 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
 	}
 	}
 
 
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionAuthenticationProvider} with the provided parameters
+	 * Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters
 	 *
 	 *
 	 * The given {@link RestOperations} should perform its own client authentication against the
 	 * The given {@link RestOperations} should perform its own client authentication against the
 	 * introspection endpoint.
 	 * introspection endpoint.
@@ -122,7 +130,7 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
 	 * {@inheritDoc}
 	 * {@inheritDoc}
 	 */
 	 */
 	@Override
 	@Override
-	public Map<String, Object> introspect(String token) {
+	public OAuth2AuthenticatedPrincipal introspect(String token) {
 		RequestEntity<?> requestEntity = this.requestEntityConverter.convert(token);
 		RequestEntity<?> requestEntity = this.requestEntityConverter.convert(token);
 		if (requestEntity == null) {
 		if (requestEntity == null) {
 			throw new OAuth2IntrospectionException("Provided token [" + token + "] isn't active");
 			throw new OAuth2IntrospectionException("Provided token [" + token + "] isn't active");
@@ -189,7 +197,8 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
 		return (TokenIntrospectionSuccessResponse) introspectionResponse;
 		return (TokenIntrospectionSuccessResponse) introspectionResponse;
 	}
 	}
 
 
-	private Map<String, Object> convertClaimsSet(TokenIntrospectionSuccessResponse response) {
+	private OAuth2AuthenticatedPrincipal convertClaimsSet(TokenIntrospectionSuccessResponse response) {
+		Collection<GrantedAuthority> authorities = new ArrayList<>();
 		Map<String, Object> claims = response.toJSONObject();
 		Map<String, Object> claims = response.toJSONObject();
 		if (response.getAudience() != null) {
 		if (response.getAudience() != null) {
 			List<String> audiences = new ArrayList<>();
 			List<String> audiences = new ArrayList<>();
@@ -216,10 +225,15 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
 			claims.put(NOT_BEFORE, response.getNotBeforeTime().toInstant());
 			claims.put(NOT_BEFORE, response.getNotBeforeTime().toInstant());
 		}
 		}
 		if (response.getScope() != null) {
 		if (response.getScope() != null) {
-			claims.put(SCOPE, Collections.unmodifiableList(response.getScope().toStringList()));
+			List<String> scopes = Collections.unmodifiableList(response.getScope().toStringList());
+			claims.put(SCOPE, scopes);
+
+			for (String scope : scopes) {
+				authorities.add(new SimpleGrantedAuthority(this.authorityPrefix + scope));
+			}
 		}
 		}
 
 
-		return claims;
+		return new DefaultOAuth2AuthenticatedPrincipal(claims, authorities);
 	}
 	}
 
 
 	private URL issuer(String uri) {
 	private URL issuer(String uri) {

+ 20 - 6
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java

@@ -19,6 +19,8 @@ package org.springframework.security.oauth2.server.resource.introspection;
 import java.net.URI;
 import java.net.URI;
 import java.net.URL;
 import java.net.URL;
 import java.time.Instant;
 import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -34,6 +36,10 @@ import reactor.core.publisher.Mono;
 
 
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.util.Assert;
 import org.springframework.util.Assert;
 import org.springframework.web.reactive.function.BodyInserters;
 import org.springframework.web.reactive.function.BodyInserters;
 import org.springframework.web.reactive.function.client.ClientResponse;
 import org.springframework.web.reactive.function.client.ClientResponse;
@@ -59,8 +65,10 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
 	private URI introspectionUri;
 	private URI introspectionUri;
 	private WebClient webClient;
 	private WebClient webClient;
 
 
+	private String authorityPrefix = "SCOPE_";
+
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionReactiveAuthenticationManager} with the provided parameters
+	 * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided parameters
 	 *
 	 *
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param clientId The client id authorized to introspect
 	 * @param clientId The client id authorized to introspect
@@ -78,7 +86,7 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
 	}
 	}
 
 
 	/**
 	/**
-	 * Creates a {@code OAuth2IntrospectionReactiveAuthenticationManager} with the provided parameters
+	 * Creates a {@code OpaqueTokenReactiveAuthenticationManager} with the provided parameters
 	 *
 	 *
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param introspectionUri The introspection endpoint uri
 	 * @param webClient The client for performing the introspection request
 	 * @param webClient The client for performing the introspection request
@@ -95,7 +103,7 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
 	 * {@inheritDoc}
 	 * {@inheritDoc}
 	 */
 	 */
 	@Override
 	@Override
-	public Mono<Map<String, Object>> introspect(String token) {
+	public Mono<OAuth2AuthenticatedPrincipal> introspect(String token) {
 		return Mono.just(token)
 		return Mono.just(token)
 				.flatMap(this::makeRequest)
 				.flatMap(this::makeRequest)
 				.flatMap(this::adaptToNimbusResponse)
 				.flatMap(this::adaptToNimbusResponse)
@@ -150,8 +158,9 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
 		}
 		}
 	}
 	}
 
 
-	private Map<String, Object> convertClaimsSet(TokenIntrospectionSuccessResponse response) {
+	private OAuth2AuthenticatedPrincipal convertClaimsSet(TokenIntrospectionSuccessResponse response) {
 		Map<String, Object> claims = response.toJSONObject();
 		Map<String, Object> claims = response.toJSONObject();
+		Collection<GrantedAuthority> authorities = new ArrayList<>();
 		if (response.getAudience() != null) {
 		if (response.getAudience() != null) {
 			List<String> audiences = new ArrayList<>();
 			List<String> audiences = new ArrayList<>();
 			for (Audience audience : response.getAudience()) {
 			for (Audience audience : response.getAudience()) {
@@ -177,10 +186,15 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
 			claims.put(NOT_BEFORE, response.getNotBeforeTime().toInstant());
 			claims.put(NOT_BEFORE, response.getNotBeforeTime().toInstant());
 		}
 		}
 		if (response.getScope() != null) {
 		if (response.getScope() != null) {
-			claims.put(SCOPE, Collections.unmodifiableList(response.getScope().toStringList()));
+			List<String> scopes = Collections.unmodifiableList(response.getScope().toStringList());
+			claims.put(SCOPE, scopes);
+
+			for (String scope : scopes) {
+				authorities.add(new SimpleGrantedAuthority(this.authorityPrefix + scope));
+			}
 		}
 		}
 
 
-		return claims;
+		return new DefaultOAuth2AuthenticatedPrincipal(claims, authorities);
 	}
 	}
 
 
 	private URL issuer(String uri) {
 	private URL issuer(String uri) {

+ 3 - 1
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/OpaqueTokenIntrospector.java

@@ -18,6 +18,8 @@ package org.springframework.security.oauth2.server.resource.introspection;
 
 
 import java.util.Map;
 import java.util.Map;
 
 
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+
 /**
 /**
  * A contract for introspecting and verifying an OAuth 2.0 token.
  * A contract for introspecting and verifying an OAuth 2.0 token.
  *
  *
@@ -41,5 +43,5 @@ public interface OpaqueTokenIntrospector {
 	 * @param token the token to introspect
 	 * @param token the token to introspect
 	 * @return the token's attributes
 	 * @return the token's attributes
 	 */
 	 */
-	Map<String, Object> introspect(String token);
+	OAuth2AuthenticatedPrincipal introspect(String token);
 }
 }

+ 3 - 1
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/ReactiveOpaqueTokenIntrospector.java

@@ -20,6 +20,8 @@ import java.util.Map;
 
 
 import reactor.core.publisher.Mono;
 import reactor.core.publisher.Mono;
 
 
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+
 /**
 /**
  * A contract for introspecting and verifying an OAuth 2.0 token.
  * A contract for introspecting and verifying an OAuth 2.0 token.
  *
  *
@@ -43,5 +45,5 @@ public interface ReactiveOpaqueTokenIntrospector {
 	 * @param token the token to introspect
 	 * @param token the token to introspect
 	 * @return the token's attributes
 	 * @return the token's attributes
 	 */
 	 */
-	Mono<Map<String, Object>> introspect(String token);
+	Mono<OAuth2AuthenticatedPrincipal> introspect(String token);
 }
 }

+ 69 - 0
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/core/TestOAuth2AuthenticatedPrincipals.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.oauth2.core;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URL;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
+
+/**
+ * Test values of {@link OAuth2AuthenticatedPrincipal}s
+ *
+ * @author Josh Cummings
+ */
+public class TestOAuth2AuthenticatedPrincipals {
+	public static OAuth2AuthenticatedPrincipal active() {
+		return active(attributes -> {});
+	}
+
+	public static OAuth2AuthenticatedPrincipal active(Consumer<Map<String, Object>> attributesConsumer) {
+		Map<String, Object> attributes = new HashMap<>();
+		attributes.put(OAuth2IntrospectionClaimNames.ACTIVE, true);
+		attributes.put(OAuth2IntrospectionClaimNames.AUDIENCE, Arrays.asList("https://protected.example.net/resource"));
+		attributes.put(OAuth2IntrospectionClaimNames.CLIENT_ID, "l238j323ds-23ij4");
+		attributes.put(OAuth2IntrospectionClaimNames.EXPIRES_AT, Instant.ofEpochSecond(1419356238));
+		attributes.put(OAuth2IntrospectionClaimNames.NOT_BEFORE, Instant.ofEpochSecond(29348723984L));
+		attributes.put(OAuth2IntrospectionClaimNames.ISSUER, url("https://server.example.com/"));
+		attributes.put(OAuth2IntrospectionClaimNames.SCOPE, Arrays.asList("read", "write", "dolphin"));
+		attributes.put(OAuth2IntrospectionClaimNames.SUBJECT, "Z5O3upPC88QrAjx00dis");
+		attributes.put(OAuth2IntrospectionClaimNames.USERNAME, "jdoe");
+		attributesConsumer.accept(attributes);
+
+		Collection<GrantedAuthority> authorities =
+				Arrays.asList(new SimpleGrantedAuthority("SCOPE_read"),
+						new SimpleGrantedAuthority("SCOPE_write"), new SimpleGrantedAuthority("SCOPE_dolphin"));
+		return new DefaultOAuth2AuthenticatedPrincipal(attributes, authorities);
+	}
+
+	private static URL url(String url) {
+		try {
+			return new URL(url);
+		} catch (IOException e) {
+			throw new UncheckedIOException(e);
+		}
+	}
+}

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

@@ -1,150 +0,0 @@
-/*
- * Copyright 2002-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.oauth2.server.resource.authentication;
-
-import java.net.URL;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.minidev.json.JSONObject;
-import org.junit.Before;
-import org.junit.Test;
-
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.AuthorityUtils;
-import org.springframework.security.oauth2.core.OAuth2AccessToken;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.CLIENT_ID;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
-
-/**
- * Tests for {@link OAuth2IntrospectionAuthenticationToken}
- *
- * @author Josh Cummings
- */
-public class OAuth2IntrospectionAuthenticationTokenTests {
-	private final OAuth2AccessToken token =
-			new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
-				"token", Instant.now(), Instant.now().plusSeconds(3600));
-	private final String name = "sub";
-	private Map<String, Object> attributesMap = new HashMap<>();
-	private final OAuth2TokenAttributes attributes = new OAuth2TokenAttributes(attributesMap);
-
-	@Before
-	public void setUp() {
-		this.attributesMap.put(SUBJECT, this.name);
-		this.attributesMap.put(CLIENT_ID, "client_id");
-		this.attributesMap.put(USERNAME, "username");
-	}
-
-	@Test
-	public void getNameWhenConfiguredInConstructorThenReturnsName() {
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes,
-						AuthorityUtils.createAuthorityList("USER"), this.name);
-		assertThat(authenticated.getName()).isEqualTo(this.name);
-	}
-
-	@Test
-	public void getNameWhenHasNoSubjectThenReturnsNull() {
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token,
-						new OAuth2TokenAttributes(Collections.singletonMap("claim", "value")),
-						Collections.emptyList());
-		assertThat(authenticated.getName()).isNull();
-	}
-
-	@Test
-	public void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes, Collections.emptyList());
-		assertThat(authenticated.getName()).isEqualTo(this.attributes.getAttribute(SUBJECT));
-	}
-
-	@Test
-	public void constructorWhenTokenIsNullThenThrowsException() {
-		assertThatCode(() -> new OAuth2IntrospectionAuthenticationToken(null, this.attributes, null))
-				.isInstanceOf(IllegalArgumentException.class)
-				.hasMessageContaining("token cannot be null");
-	}
-
-	@Test
-	public void constructorWhenAttributesAreNullOrEmptyThenThrowsException() {
-		assertThatCode(() -> new OAuth2IntrospectionAuthenticationToken(this.token, null, null))
-				.isInstanceOf(IllegalArgumentException.class)
-				.hasMessageContaining("principal cannot be null");
-
-		assertThatCode(() -> new OAuth2IntrospectionAuthenticationToken(this.token,
-									new OAuth2TokenAttributes(Collections.emptyMap()), null))
-				.isInstanceOf(IllegalArgumentException.class)
-				.hasMessageContaining("attributes cannot be empty");
-	}
-
-	@Test
-	public void constructorWhenPassingAllAttributesThenTokenIsAuthenticated() {
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token,
-						new OAuth2TokenAttributes(Collections.singletonMap("claim", "value")),
-						Collections.emptyList(), "harris");
-		assertThat(authenticated.isAuthenticated()).isTrue();
-	}
-
-	@Test
-	public void getTokenAttributesWhenHasTokenThenReturnsThem() {
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes, Collections.emptyList());
-		assertThat(authenticated.getTokenAttributes()).isEqualTo(this.attributes.getAttributes());
-	}
-
-	@Test
-	public void getAuthoritiesWhenHasAuthoritiesThenReturnsThem() {
-		List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("USER");
-		OAuth2IntrospectionAuthenticationToken authenticated =
-				new OAuth2IntrospectionAuthenticationToken(this.token, this.attributes, authorities);
-		assertThat(authenticated.getAuthorities()).isEqualTo(authorities);
-	}
-
-	// gh-6843
-	@Test
-	public void constructorWhenDefaultParametersThenSetsPrincipalToAttributesCopy() {
-		JSONObject attributes = new JSONObject();
-		attributes.put("active", true);
-		OAuth2IntrospectionAuthenticationToken token =
-				new OAuth2IntrospectionAuthenticationToken(this.token, new OAuth2TokenAttributes(attributes),
-						Collections.emptyList());
-		assertThat(token.getPrincipal()).isNotSameAs(attributes);
-		assertThat(token.getTokenAttributes()).isNotSameAs(attributes);
-	}
-
-	// gh-6843
-	@Test
-	public void toStringWhenAttributesContainsURLThenDoesNotFail() throws Exception {
-		JSONObject attributes = new JSONObject(Collections.singletonMap("iss", new URL("https://idp.example.com")));
-		OAuth2IntrospectionAuthenticationToken token =
-				new OAuth2IntrospectionAuthenticationToken(this.token, new OAuth2TokenAttributes(attributes),
-						Collections.emptyList());
-		assertThatCode(token::toString)
-				.doesNotThrowAnyException();
-	}
-}

+ 23 - 27
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionAuthenticationProviderTests.java → oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProviderTests.java

@@ -18,23 +18,26 @@ package org.springframework.security.oauth2.server.resource.authentication;
 import java.net.URL;
 import java.net.URL;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Map;
 
 
 import org.junit.Test;
 import org.junit.Test;
 
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
-import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.when;
+import static org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals.active;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ACTIVE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ACTIVE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
@@ -43,30 +46,26 @@ import static org.springframework.security.oauth2.server.resource.introspection.
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
-import static org.springframework.security.oauth2.server.resource.introspection.TestOAuth2TokenIntrospectionClientResponses.active;
 
 
 /**
 /**
- * Tests for {@link OAuth2IntrospectionAuthenticationProvider}
+ * Tests for {@link OpaqueTokenAuthenticationProvider}
  *
  *
  * @author Josh Cummings
  * @author Josh Cummings
- * @since 5.2
  */
  */
-public class OAuth2IntrospectionAuthenticationProviderTests {
+public class OpaqueTokenAuthenticationProviderTests {
 	@Test
 	@Test
 	public void authenticateWhenActiveTokenThenOk() throws Exception {
 	public void authenticateWhenActiveTokenThenOk() throws Exception {
-		Map<String, Object> claims = active();
-		claims.put("extension_field", "twenty-seven");
-		OpaqueTokenIntrospector introspectionClient = mock(OpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any())).thenReturn(claims);
-		OAuth2IntrospectionAuthenticationProvider provider =
-				new OAuth2IntrospectionAuthenticationProvider(introspectionClient);
+		OAuth2AuthenticatedPrincipal principal = active(attributes -> attributes.put("extension_field", "twenty-seven"));
+		OpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);
+		when(introspector.introspect(any())).thenReturn(principal);
+		OpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);
 
 
 		Authentication result =
 		Authentication result =
 				provider.authenticate(new BearerTokenAuthenticationToken("token"));
 				provider.authenticate(new BearerTokenAuthenticationToken("token"));
 
 
-		assertThat(result.getPrincipal()).isInstanceOf(OAuth2TokenAttributes.class);
+		assertThat(result.getPrincipal()).isInstanceOf(DefaultOAuth2AuthenticatedPrincipal.class);
 
 
-		Map<String, Object> attributes = ((OAuth2TokenAttributes) result.getPrincipal()).getAttributes();
+		Map<String, Object> attributes = ((DefaultOAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
 		assertThat(attributes)
 		assertThat(attributes)
 				.isNotNull()
 				.isNotNull()
 				.containsEntry(ACTIVE, true)
 				.containsEntry(ACTIVE, true)
@@ -86,18 +85,16 @@ public class OAuth2IntrospectionAuthenticationProviderTests {
 
 
 	@Test
 	@Test
 	public void authenticateWhenMissingScopeAttributeThenNoAuthorities() {
 	public void authenticateWhenMissingScopeAttributeThenNoAuthorities() {
-		Map<String, Object> claims = active();
-		claims.remove(SCOPE);
-		OpaqueTokenIntrospector introspectionClient = mock(OpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any())).thenReturn(claims);
-		OAuth2IntrospectionAuthenticationProvider provider =
-				new OAuth2IntrospectionAuthenticationProvider(introspectionClient);
+		OAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(Collections.singletonMap("claim", "value"), null);
+		OpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);
+		when(introspector.introspect(any())).thenReturn(principal);
+		OpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);
 
 
 		Authentication result =
 		Authentication result =
 				provider.authenticate(new BearerTokenAuthenticationToken("token"));
 				provider.authenticate(new BearerTokenAuthenticationToken("token"));
-		assertThat(result.getPrincipal()).isInstanceOf(OAuth2TokenAttributes.class);
+		assertThat(result.getPrincipal()).isInstanceOf(OAuth2AuthenticatedPrincipal.class);
 
 
-		Map<String, Object> attributes = ((OAuth2TokenAttributes) result.getPrincipal()).getAttributes();
+		Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
 		assertThat(attributes)
 		assertThat(attributes)
 				.isNotNull()
 				.isNotNull()
 				.doesNotContainKey(SCOPE);
 				.doesNotContainKey(SCOPE);
@@ -107,10 +104,9 @@ public class OAuth2IntrospectionAuthenticationProviderTests {
 
 
 	@Test
 	@Test
 	public void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {
 	public void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {
-		OpaqueTokenIntrospector introspectionClient = mock(OpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any())).thenThrow(new OAuth2IntrospectionException("with \"invalid\" chars"));
-		OAuth2IntrospectionAuthenticationProvider provider =
-				new OAuth2IntrospectionAuthenticationProvider(introspectionClient);
+		OpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);
+		when(introspector.introspect(any())).thenThrow(new OAuth2IntrospectionException("with \"invalid\" chars"));
+		OpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);
 
 
 		assertThatCode(() -> provider.authenticate(new BearerTokenAuthenticationToken("token")))
 		assertThatCode(() -> provider.authenticate(new BearerTokenAuthenticationToken("token")))
 				.isInstanceOf(OAuth2AuthenticationException.class)
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -120,7 +116,7 @@ public class OAuth2IntrospectionAuthenticationProviderTests {
 
 
 	@Test
 	@Test
 	public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
 	public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
-		assertThatCode(() -> new OAuth2IntrospectionAuthenticationProvider(null))
+		assertThatCode(() -> new OpaqueTokenAuthenticationProvider(null))
 				.isInstanceOf(IllegalArgumentException.class);
 				.isInstanceOf(IllegalArgumentException.class);
 	}
 	}
 }
 }

+ 28 - 26
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OAuth2IntrospectionReactiveAuthenticationManagerTests.java → oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManagerTests.java

@@ -19,24 +19,27 @@ package org.springframework.security.oauth2.server.resource.authentication;
 import java.net.URL;
 import java.net.URL;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Map;
 
 
 import org.junit.Test;
 import org.junit.Test;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
 import reactor.core.publisher.Mono;
 import reactor.core.publisher.Mono;
 
 
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
 import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
 import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
 import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector;
-import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.when;
+import static org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals.active;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ACTIVE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ACTIVE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
@@ -45,27 +48,27 @@ import static org.springframework.security.oauth2.server.resource.introspection.
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
 import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
-import static org.springframework.security.oauth2.server.resource.introspection.TestOAuth2TokenIntrospectionClientResponses.active;
 
 
 /**
 /**
- * Tests for {@link OAuth2IntrospectionReactiveAuthenticationManager}
+ * Tests for {@link OpaqueTokenReactiveAuthenticationManager}
+ *
+ * @author Josh Cummings
  */
  */
-public class OAuth2IntrospectionReactiveAuthenticationManagerTests {
+public class OpaqueTokenReactiveAuthenticationManagerTests {
 	@Test
 	@Test
 	public void authenticateWhenActiveTokenThenOk() throws Exception {
 	public void authenticateWhenActiveTokenThenOk() throws Exception {
-		Map<String, Object> claims = active();
-		claims.put("extension_field", "twenty-seven");
-		ReactiveOpaqueTokenIntrospector introspectionClient = mock(ReactiveOpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any())).thenReturn(Mono.just(claims));
-		OAuth2IntrospectionReactiveAuthenticationManager provider =
-				new OAuth2IntrospectionReactiveAuthenticationManager(introspectionClient);
+		OAuth2AuthenticatedPrincipal authority = active(attributes -> attributes.put("extension_field", "twenty-seven"));
+		ReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);
+		when(introspector.introspect(any())).thenReturn(Mono.just(authority));
+		OpaqueTokenReactiveAuthenticationManager provider =
+				new OpaqueTokenReactiveAuthenticationManager(introspector);
 
 
 		Authentication result =
 		Authentication result =
 				provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
 				provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
 
 
-		assertThat(result.getPrincipal()).isInstanceOf(OAuth2TokenAttributes.class);
+		assertThat(result.getPrincipal()).isInstanceOf(DefaultOAuth2AuthenticatedPrincipal.class);
 
 
-		Map<String, Object> attributes = ((OAuth2TokenAttributes) result.getPrincipal()).getAttributes();
+		Map<String, Object> attributes = ((DefaultOAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
 		assertThat(attributes)
 		assertThat(attributes)
 				.isNotNull()
 				.isNotNull()
 				.containsEntry(ACTIVE, true)
 				.containsEntry(ACTIVE, true)
@@ -85,18 +88,17 @@ public class OAuth2IntrospectionReactiveAuthenticationManagerTests {
 
 
 	@Test
 	@Test
 	public void authenticateWhenMissingScopeAttributeThenNoAuthorities() {
 	public void authenticateWhenMissingScopeAttributeThenNoAuthorities() {
-		Map<String, Object> claims = active();
-		claims.remove(SCOPE);
-		ReactiveOpaqueTokenIntrospector introspectionClient = mock(ReactiveOpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any())).thenReturn(Mono.just(claims));
-		OAuth2IntrospectionReactiveAuthenticationManager provider =
-				new OAuth2IntrospectionReactiveAuthenticationManager(introspectionClient);
+		OAuth2AuthenticatedPrincipal authority = new DefaultOAuth2AuthenticatedPrincipal(Collections.singletonMap("claim", "value"), null);
+		ReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);
+		when(introspector.introspect(any())).thenReturn(Mono.just(authority));
+		OpaqueTokenReactiveAuthenticationManager provider =
+				new OpaqueTokenReactiveAuthenticationManager(introspector);
 
 
 		Authentication result =
 		Authentication result =
 				provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
 				provider.authenticate(new BearerTokenAuthenticationToken("token")).block();
-		assertThat(result.getPrincipal()).isInstanceOf(OAuth2TokenAttributes.class);
+		assertThat(result.getPrincipal()).isInstanceOf(DefaultOAuth2AuthenticatedPrincipal.class);
 
 
-		Map<String, Object> attributes = ((OAuth2TokenAttributes) result.getPrincipal()).getAttributes();
+		Map<String, Object> attributes = ((DefaultOAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
 		assertThat(attributes)
 		assertThat(attributes)
 				.isNotNull()
 				.isNotNull()
 				.doesNotContainKey(SCOPE);
 				.doesNotContainKey(SCOPE);
@@ -106,11 +108,11 @@ public class OAuth2IntrospectionReactiveAuthenticationManagerTests {
 
 
 	@Test
 	@Test
 	public void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {
 	public void authenticateWhenIntrospectionEndpointThrowsExceptionThenInvalidToken() {
-		ReactiveOpaqueTokenIntrospector introspectionClient = mock(ReactiveOpaqueTokenIntrospector.class);
-		when(introspectionClient.introspect(any()))
+		ReactiveOpaqueTokenIntrospector introspector = mock(ReactiveOpaqueTokenIntrospector.class);
+		when(introspector.introspect(any()))
 				.thenReturn(Mono.error(new OAuth2IntrospectionException("with \"invalid\" chars")));
 				.thenReturn(Mono.error(new OAuth2IntrospectionException("with \"invalid\" chars")));
-		OAuth2IntrospectionReactiveAuthenticationManager provider =
-				new OAuth2IntrospectionReactiveAuthenticationManager(introspectionClient);
+		OpaqueTokenReactiveAuthenticationManager provider =
+				new OpaqueTokenReactiveAuthenticationManager(introspector);
 
 
 		assertThatCode(() -> provider.authenticate(new BearerTokenAuthenticationToken("token")).block())
 		assertThatCode(() -> provider.authenticate(new BearerTokenAuthenticationToken("token")).block())
 				.isInstanceOf(OAuth2AuthenticationException.class)
 				.isInstanceOf(OAuth2AuthenticationException.class)
@@ -120,7 +122,7 @@ public class OAuth2IntrospectionReactiveAuthenticationManagerTests {
 
 
 	@Test
 	@Test
 	public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
 	public void constructorWhenIntrospectionClientIsNullThenIllegalArgumentException() {
-		assertThatCode(() -> new OAuth2IntrospectionReactiveAuthenticationManager(null))
+		assertThatCode(() -> new OpaqueTokenReactiveAuthenticationManager(null))
 				.isInstanceOf(IllegalArgumentException.class);
 				.isInstanceOf(IllegalArgumentException.class);
 	}
 	}
 }
 }

+ 5 - 4
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospectorTests.java

@@ -38,6 +38,7 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.http.RequestEntity;
 import org.springframework.http.RequestEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.web.client.RestOperations;
 import org.springframework.web.client.RestOperations;
 
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -113,8 +114,8 @@ public class NimbusOpaqueTokenIntrospectorTests {
 			OpaqueTokenIntrospector introspectionClient =
 			OpaqueTokenIntrospector introspectionClient =
 					new NimbusOpaqueTokenIntrospector(introspectUri, CLIENT_ID, CLIENT_SECRET);
 					new NimbusOpaqueTokenIntrospector(introspectUri, CLIENT_ID, CLIENT_SECRET);
 
 
-			Map<String, Object> attributes = introspectionClient.introspect("token");
-			assertThat(attributes)
+			OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
+			assertThat(authority.getAttributes())
 					.isNotNull()
 					.isNotNull()
 					.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 					.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 					.containsEntry(AUDIENCE, Arrays.asList("https://protected.example.net/resource"))
 					.containsEntry(AUDIENCE, Arrays.asList("https://protected.example.net/resource"))
@@ -168,8 +169,8 @@ public class NimbusOpaqueTokenIntrospectorTests {
 		when(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
 		when(restOperations.exchange(any(RequestEntity.class), eq(String.class)))
 				.thenReturn(response(new JSONObject(introspectedValues).toJSONString()));
 				.thenReturn(response(new JSONObject(introspectedValues).toJSONString()));
 
 
-		Map<String, Object> attributes = introspectionClient.introspect("token");
-		assertThat(attributes)
+		OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token");
+		assertThat(authority.getAttributes())
 				.isNotNull()
 				.isNotNull()
 				.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 				.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 				.containsEntry(AUDIENCE, Arrays.asList("aud"))
 				.containsEntry(AUDIENCE, Arrays.asList("aud"))

+ 5 - 4
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java

@@ -36,6 +36,7 @@ import reactor.core.publisher.Mono;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.web.reactive.function.client.ClientResponse;
 import org.springframework.web.reactive.function.client.ClientResponse;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.reactive.function.client.WebClient;
 
 
@@ -103,8 +104,8 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
 			NimbusReactiveOpaqueTokenIntrospector introspectionClient =
 			NimbusReactiveOpaqueTokenIntrospector introspectionClient =
 					new NimbusReactiveOpaqueTokenIntrospector(introspectUri, CLIENT_ID, CLIENT_SECRET);
 					new NimbusReactiveOpaqueTokenIntrospector(introspectUri, CLIENT_ID, CLIENT_SECRET);
 
 
-			Map<String, Object> attributes = introspectionClient.introspect("token").block();
-			assertThat(attributes)
+			OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
+			assertThat(authority.getAttributes())
 					.isNotNull()
 					.isNotNull()
 					.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 					.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 					.containsEntry(AUDIENCE, Arrays.asList("https://protected.example.net/resource"))
 					.containsEntry(AUDIENCE, Arrays.asList("https://protected.example.net/resource"))
@@ -155,8 +156,8 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
 		NimbusReactiveOpaqueTokenIntrospector introspectionClient =
 		NimbusReactiveOpaqueTokenIntrospector introspectionClient =
 				new NimbusReactiveOpaqueTokenIntrospector(INTROSPECTION_URL, webClient);
 				new NimbusReactiveOpaqueTokenIntrospector(INTROSPECTION_URL, webClient);
 
 
-		Map<String, Object> attributes = introspectionClient.introspect("token").block();
-		assertThat(attributes)
+		OAuth2AuthenticatedPrincipal authority = introspectionClient.introspect("token").block();
+		assertThat(authority.getAttributes())
 				.isNotNull()
 				.isNotNull()
 				.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 				.containsEntry(OAuth2IntrospectionClaimNames.ACTIVE, true)
 				.containsEntry(AUDIENCE, Arrays.asList("aud"))
 				.containsEntry(AUDIENCE, Arrays.asList("aud"))

+ 0 - 59
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/TestOAuth2TokenIntrospectionClientResponses.java

@@ -1,59 +0,0 @@
-/*
- * Copyright 2002-2019 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.oauth2.server.resource.introspection;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.URL;
-import java.time.Instant;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ACTIVE;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.AUDIENCE;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.CLIENT_ID;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.EXPIRES_AT;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.ISSUER;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.NOT_BEFORE;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SCOPE;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
-import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
-
-public class TestOAuth2TokenIntrospectionClientResponses {
-	public static Map<String, Object> active() {
-		Map<String, Object> attributes = new HashMap<>();
-		attributes.put(ACTIVE, true);
-		attributes.put(AUDIENCE, Arrays.asList("https://protected.example.net/resource"));
-		attributes.put(CLIENT_ID, "l238j323ds-23ij4");
-		attributes.put(EXPIRES_AT, Instant.ofEpochSecond(1419356238));
-		attributes.put(NOT_BEFORE, Instant.ofEpochSecond(29348723984L));
-		attributes.put(ISSUER, url("https://server.example.com/"));
-		attributes.put(SCOPE, Arrays.asList("read", "write", "dolphin"));
-		attributes.put(SUBJECT, "Z5O3upPC88QrAjx00dis");
-		attributes.put(USERNAME, "jdoe");
-		return attributes;
-	}
-
-	private static URL url(String url) {
-		try {
-			return new URL(url);
-		} catch (IOException e) {
-			throw new UncheckedIOException(e);
-		}
-	}
-}

+ 3 - 3
samples/boot/oauth2resourceserver-opaque/src/main/java/sample/OAuth2ResourceServerController.java

@@ -16,7 +16,7 @@
 package sample;
 package sample;
 
 
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.security.oauth2.core.OAuth2TokenAttributes;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 
 
@@ -27,8 +27,8 @@ import org.springframework.web.bind.annotation.RestController;
 public class OAuth2ResourceServerController {
 public class OAuth2ResourceServerController {
 
 
 	@GetMapping("/")
 	@GetMapping("/")
-	public String index(@AuthenticationPrincipal OAuth2TokenAttributes attributes) {
-		return String.format("Hello, %s!", (String) attributes.getAttribute("sub"));
+	public String index(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {
+		return String.format("Hello, %s!", (String) principal.getAttribute("sub"));
 	}
 	}
 
 
 	@GetMapping("/message")
 	@GetMapping("/message")