Browse Source

Remove Reliance on BearerTokenResolver

Closes gh-9186
Josh Cummings 4 years ago
parent
commit
af669a2166

+ 32 - 34
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java

@@ -34,12 +34,13 @@ import org.springframework.core.log.LogMessage;
 import org.springframework.lang.NonNull;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jwt.JwtDecoder;
 import org.springframework.security.oauth2.jwt.JwtDecoders;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
-import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
-import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
 import org.springframework.util.Assert;
 
 /**
@@ -63,9 +64,7 @@ import org.springframework.util.Assert;
  */
 public final class JwtIssuerAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
 
-	private final AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
-
-	private Converter<HttpServletRequest, String> issuerConverter = new JwtClaimIssuerConverter();
+	private final AuthenticationManager authenticationManager;
 
 	/**
 	 * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided
@@ -83,8 +82,9 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
 	 */
 	public JwtIssuerAuthenticationManagerResolver(Collection<String> trustedIssuers) {
 		Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
-		this.issuerAuthenticationManagerResolver = new TrustedIssuerJwtAuthenticationManagerResolver(
-				Collections.unmodifiableCollection(trustedIssuers)::contains);
+		this.authenticationManager = new ResolvingAuthenticationManager(
+				new TrustedIssuerJwtAuthenticationManagerResolver(
+						Collections.unmodifiableCollection(trustedIssuers)::contains));
 	}
 
 	/**
@@ -111,7 +111,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
 	public JwtIssuerAuthenticationManagerResolver(
 			AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {
 		Assert.notNull(issuerAuthenticationManagerResolver, "issuerAuthenticationManagerResolver cannot be null");
-		this.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;
+		this.authenticationManager = new ResolvingAuthenticationManager(issuerAuthenticationManagerResolver);
 	}
 
 	/**
@@ -122,40 +122,39 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
 	 */
 	@Override
 	public AuthenticationManager resolve(HttpServletRequest request) {
-		String issuer = this.issuerConverter.convert(request);
-		AuthenticationManager authenticationManager = this.issuerAuthenticationManagerResolver.resolve(issuer);
-		if (authenticationManager == null) {
-			throw new InvalidBearerTokenException("Invalid issuer");
-		}
-		return authenticationManager;
+		return this.authenticationManager;
 	}
 
-	/**
-	 * Set a custom bearer token resolver
-	 *
-	 * @since 5.5
-	 */
-	public void setBearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
-		Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
-		this.issuerConverter = new JwtClaimIssuerConverter(bearerTokenResolver);
-	}
+	private static class ResolvingAuthenticationManager implements AuthenticationManager {
 
-	private static class JwtClaimIssuerConverter implements Converter<HttpServletRequest, String> {
+		private final Converter<BearerTokenAuthenticationToken, String> issuerConverter = new JwtClaimIssuerConverter();
 
-		private final BearerTokenResolver resolver;
+		private final AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
 
-		JwtClaimIssuerConverter() {
-			this(new DefaultBearerTokenResolver());
+		ResolvingAuthenticationManager(AuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {
+			this.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;
 		}
 
-		JwtClaimIssuerConverter(BearerTokenResolver bearerTokenResolver) {
-			Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
-			this.resolver = bearerTokenResolver;
+		@Override
+		public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+			Assert.isTrue(authentication instanceof BearerTokenAuthenticationToken,
+					"Authentication must be of type BearerTokenAuthenticationToken");
+			BearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) authentication;
+			String issuer = this.issuerConverter.convert(token);
+			AuthenticationManager authenticationManager = this.issuerAuthenticationManagerResolver.resolve(issuer);
+			if (authenticationManager == null) {
+				throw new InvalidBearerTokenException("Invalid issuer");
+			}
+			return authenticationManager.authenticate(authentication);
 		}
 
+	}
+
+	private static class JwtClaimIssuerConverter implements Converter<BearerTokenAuthenticationToken, String> {
+
 		@Override
-		public String convert(@NonNull HttpServletRequest request) {
-			String token = this.resolver.resolve(request);
+		public String convert(@NonNull BearerTokenAuthenticationToken authentication) {
+			String token = authentication.getToken();
 			try {
 				String issuer = JWTParser.parse(token).getJWTClaimsSet().getIssuer();
 				if (issuer != null) {
@@ -170,8 +169,7 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat
 
 	}
 
-	private static class TrustedIssuerJwtAuthenticationManagerResolver
-			implements AuthenticationManagerResolver<String> {
+	static class TrustedIssuerJwtAuthenticationManagerResolver implements AuthenticationManagerResolver<String> {
 
 		private final Log logger = LogFactory.getLog(getClass());
 

+ 37 - 47
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java

@@ -32,11 +32,11 @@ import org.springframework.lang.NonNull;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
 import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
 import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
-import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 
@@ -63,9 +63,7 @@ import org.springframework.web.server.ServerWebExchange;
 public final class JwtIssuerReactiveAuthenticationManagerResolver
 		implements ReactiveAuthenticationManagerResolver<ServerWebExchange> {
 
-	private final ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
-
-	private Converter<ServerWebExchange, Mono<String>> issuerConverter = new JwtClaimIssuerConverter();
+	private final ReactiveAuthenticationManager authenticationManager;
 
 	/**
 	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
@@ -83,8 +81,8 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
 	 */
 	public JwtIssuerReactiveAuthenticationManagerResolver(Collection<String> trustedIssuers) {
 		Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
-		this.issuerAuthenticationManagerResolver = new TrustedIssuerJwtAuthenticationManagerResolver(
-				new ArrayList<>(trustedIssuers)::contains);
+		this.authenticationManager = new ResolvingAuthenticationManager(
+				new TrustedIssuerJwtAuthenticationManagerResolver(new ArrayList<>(trustedIssuers)::contains));
 	}
 
 	/**
@@ -111,7 +109,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
 	public JwtIssuerReactiveAuthenticationManagerResolver(
 			ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {
 		Assert.notNull(issuerAuthenticationManagerResolver, "issuerAuthenticationManagerResolver cannot be null");
-		this.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;
+		this.authenticationManager = new ResolvingAuthenticationManager(issuerAuthenticationManagerResolver);
 	}
 
 	/**
@@ -122,61 +120,53 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
 	 */
 	@Override
 	public Mono<ReactiveAuthenticationManager> resolve(ServerWebExchange exchange) {
-		// @formatter:off
-		return this.issuerConverter.convert(exchange)
-				.flatMap((issuer) -> this.issuerAuthenticationManagerResolver
-						.resolve(issuer)
-						.switchIfEmpty(Mono.error(() -> new InvalidBearerTokenException("Invalid issuer " + issuer)))
-				);
-		// @formatter:on
+		return Mono.just(this.authenticationManager);
 	}
 
-	/**
-	 * Set a custom server bearer token authentication converter
-	 *
-	 * @since 5.5
-	 */
-	public void setServerBearerTokenAuthenticationConverter(
-			ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverter) {
-		Assert.notNull(serverBearerTokenAuthenticationConverter,
-				"serverBearerTokenAuthenticationConverter cannot be null");
-		this.issuerConverter = new JwtClaimIssuerConverter(serverBearerTokenAuthenticationConverter);
-	}
+	private static class ResolvingAuthenticationManager implements ReactiveAuthenticationManager {
+
+		private final Converter<BearerTokenAuthenticationToken, Mono<String>> issuerConverter = new JwtClaimIssuerConverter();
 
-	private static class JwtClaimIssuerConverter implements Converter<ServerWebExchange, Mono<String>> {
+		private final ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
 
-		private final ServerBearerTokenAuthenticationConverter converter;
+		ResolvingAuthenticationManager(
+				ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver) {
 
-		JwtClaimIssuerConverter() {
-			this(new ServerBearerTokenAuthenticationConverter());
+			this.issuerAuthenticationManagerResolver = issuerAuthenticationManagerResolver;
 		}
 
-		JwtClaimIssuerConverter(ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverter) {
-			Assert.notNull(serverBearerTokenAuthenticationConverter,
-					"serverBearerTokenAuthenticationConverter cannot be null");
-			this.converter = serverBearerTokenAuthenticationConverter;
+		@Override
+		public Mono<Authentication> authenticate(Authentication authentication) {
+			Assert.isTrue(authentication instanceof BearerTokenAuthenticationToken,
+					"Authentication must be of type BearerTokenAuthenticationToken");
+			BearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) authentication;
+			return this.issuerConverter.convert(token)
+					.flatMap((issuer) -> this.issuerAuthenticationManagerResolver.resolve(issuer).switchIfEmpty(
+							Mono.error(() -> new InvalidBearerTokenException("Invalid issuer " + issuer))))
+					.flatMap((manager) -> manager.authenticate(authentication));
 		}
 
+	}
+
+	private static class JwtClaimIssuerConverter implements Converter<BearerTokenAuthenticationToken, Mono<String>> {
+
 		@Override
-		public Mono<String> convert(@NonNull ServerWebExchange exchange) {
-			return this.converter.convert(exchange).map((convertedToken) -> {
-				BearerTokenAuthenticationToken token = (BearerTokenAuthenticationToken) convertedToken;
-				try {
-					String issuer = JWTParser.parse(token.getToken()).getJWTClaimsSet().getIssuer();
-					if (issuer == null) {
-						throw new InvalidBearerTokenException("Missing issuer");
-					}
-					return issuer;
+		public Mono<String> convert(@NonNull BearerTokenAuthenticationToken token) {
+			try {
+				String issuer = JWTParser.parse(token.getToken()).getJWTClaimsSet().getIssuer();
+				if (issuer == null) {
+					throw new InvalidBearerTokenException("Missing issuer");
 				}
-				catch (Exception ex) {
-					throw new InvalidBearerTokenException(ex.getMessage(), ex);
-				}
-			});
+				return Mono.just(issuer);
+			}
+			catch (Exception ex) {
+				return Mono.error(() -> new InvalidBearerTokenException(ex.getMessage(), ex));
+			}
 		}
 
 	}
 
-	private static class TrustedIssuerJwtAuthenticationManagerResolver
+	static class TrustedIssuerJwtAuthenticationManagerResolver
 			implements ReactiveAuthenticationManagerResolver<String> {
 
 		private final Map<String, Mono<ReactiveAuthenticationManager>> authenticationManagers = new ConcurrentHashMap<>();

+ 49 - 55
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java

@@ -21,8 +21,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.servlet.http.HttpServletRequest;
-
 import com.nimbusds.jose.JWSAlgorithm;
 import com.nimbusds.jose.JWSHeader;
 import com.nimbusds.jose.JWSObject;
@@ -35,21 +33,20 @@ import okhttp3.mockwebserver.MockResponse;
 import okhttp3.mockwebserver.MockWebServer;
 import org.junit.Test;
 
-import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManagerResolver;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jose.TestKeys;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
-import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
+import static org.mockito.BDDMockito.mock;
+import static org.mockito.BDDMockito.verify;
 
 /**
  * Tests for {@link JwtIssuerAuthenticationManagerResolver}
@@ -59,7 +56,7 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 	private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n" + "    \"issuer\": \"%s\", \n"
 			+ "    \"jwks_uri\": \"%s/.well-known/jwks.json\" \n" + "}";
 
-	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
+	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"}]}";
 
 	private String jwt = jwt("iss", "trusted");
 
@@ -81,17 +78,36 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 					.setHeader("Content-Type", "application/json")
 					.setBody(JWK_SET)
 			);
+			server.enqueue(new MockResponse().setResponseCode(200)
+					.setHeader("Content-Type", "application/json")
+					.setBody(JWK_SET)
+			);
 			// @formatter:on
 			JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
 					new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
 			jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
 			JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 					issuer);
-			MockHttpServletRequest request = new MockHttpServletRequest();
-			request.addHeader("Authorization", "Bearer " + jws.serialize());
-			AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(request);
+			Authentication token = withBearerToken(jws.serialize());
+			AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
 			assertThat(authenticationManager).isNotNull();
-			AuthenticationManager cachedAuthenticationManager = authenticationManagerResolver.resolve(request);
+			Authentication authentication = authenticationManager.authenticate(token);
+			assertThat(authentication.isAuthenticated()).isTrue();
+		}
+	}
+
+	@Test
+	public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
+		try (MockWebServer server = new MockWebServer()) {
+			String issuer = server.url("").toString();
+			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
+					.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
+			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
+					.setBody(JWK_SET));
+			TrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(
+					(iss) -> iss.equals(issuer));
+			AuthenticationManager authenticationManager = resolver.resolve(issuer);
+			AuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer);
 			assertThat(authenticationManager).isSameAs(cachedAuthenticationManager);
 		}
 	}
@@ -100,11 +116,10 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 	public void resolveWhenUsingUntrustedIssuerThenException() {
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				"other", "issuers");
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.jwt);
+		Authentication token = withBearerToken(this.jwt);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(request))
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
 				.withMessageContaining("Invalid issuer");
 		// @formatter:on
 	}
@@ -114,43 +129,30 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				(issuer) -> authenticationManager);
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.jwt);
-		assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager);
-	}
-
-	@Test
-	public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverAndCustomBearerTokenResolverThenUses() {
-		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
-		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
-				(issuer) -> authenticationManager);
-		BearerTokenResolver bearerTokenResolverSpy = spy(new TestBearerTokenResolver());
-		authenticationManagerResolver.setBearerTokenResolver(bearerTokenResolverSpy);
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.jwt);
-		assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager);
-		verify(bearerTokenResolverSpy).resolve(any());
+		Authentication token = withBearerToken(this.jwt);
+		authenticationManagerResolver.resolve(null).authenticate(token);
+		verify(authenticationManager).authenticate(token);
 	}
 
 	@Test
 	public void resolveWhenUsingExternalSourceThenRespondsToChanges() {
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.jwt);
+		Authentication token = withBearerToken(this.jwt);
 		Map<String, AuthenticationManager> authenticationManagers = new HashMap<>();
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				authenticationManagers::get);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(request))
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
 				.withMessageContaining("Invalid issuer");
 		// @formatter:on
 		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
 		authenticationManagers.put("trusted", authenticationManager);
-		assertThat(authenticationManagerResolver.resolve(request)).isSameAs(authenticationManager);
+		authenticationManagerResolver.resolve(null).authenticate(token);
+		verify(authenticationManager).authenticate(token);
 		authenticationManagers.clear();
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(request))
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
 				.withMessageContaining("Invalid issuer");
 		// @formatter:on
 	}
@@ -159,11 +161,10 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenMalformedThenException() {
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				"trusted");
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer jwt");
+		Authentication token = withBearerToken("jwt");
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(request))
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
 				.withMessageNotContaining("Invalid issuer");
 		// @formatter:on
 	}
@@ -172,11 +173,10 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenNoIssuerThenException() {
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				"trusted");
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.noIssuer);
+		Authentication token = withBearerToken(this.noIssuer);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(request))
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
 				.withMessageContaining("Missing issuer");
 		// @formatter:on
 	}
@@ -185,12 +185,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenEvilThenGenericException() {
 		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
 				"trusted");
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.addHeader("Authorization", "Bearer " + this.evil);
+		Authentication token = withBearerToken(this.evil);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
 				.isThrownBy(() -> authenticationManagerResolver
-						.resolve(request)
+						.resolve(null).authenticate(token)
 				)
 				.withMessage("Invalid issuer");
 		// @formatter:on
@@ -210,18 +209,13 @@ public class JwtIssuerAuthenticationManagerResolverTests {
 				.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver((AuthenticationManagerResolver) null));
 	}
 
+	private Authentication withBearerToken(String token) {
+		return new BearerTokenAuthenticationToken(token);
+	}
+
 	private String jwt(String claim, String value) {
 		PlainJWT jwt = new PlainJWT(new JWTClaimsSet.Builder().claim(claim, value).build());
 		return jwt.serialize();
 	}
 
-	static class TestBearerTokenResolver implements BearerTokenResolver {
-
-		@Override
-		public String resolve(HttpServletRequest request) {
-			return "eyJhbGciOiJub25lIn0.eyJpc3MiOiJ0cnVzdGVkIn0.";
-		}
-
-	}
-
 }

+ 60 - 48
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java

@@ -34,22 +34,22 @@ import okhttp3.mockwebserver.MockWebServer;
 import org.junit.Test;
 import reactor.core.publisher.Mono;
 
-import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
-import org.springframework.mock.web.server.MockServerWebExchange;
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jose.TestKeys;
 import org.springframework.security.oauth2.jwt.JwtClaimNames;
-import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerReactiveAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
+import static org.mockito.BDDMockito.any;
+import static org.mockito.BDDMockito.mock;
+import static org.mockito.BDDMockito.verify;
+import static org.mockito.BDDMockito.when;
 
 /**
  * Tests for {@link JwtIssuerReactiveAuthenticationManagerResolver}
@@ -63,7 +63,7 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 			+ "}";
 	// @formatter:on
 
-	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGmuLvolxsDncc2UrhyMBY6DVQVgMSVYaPCTgW76iYEKGgzTEw5IBRQL9w3SRJWd3VJTZZQjkXef48Ocz06PGF3lhbz4t5UEZtdF4rIe7u-977QwHuh7yRPBQ3sII-cVoOUMgaXB9SHcGF2iZCtPzL_IffDUcfhLQteGebhW8A6eUHgpD5A1PQ-JCw_G7UOzZAjjDjtNM2eqm8j-Ms_gqnm4MiCZ4E-9pDN77CAAPVN7kuX6ejs9KBXpk01z48i9fORYk9u7rAkh1HuQw\"}]}";
+	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"}]}";
 
 	private String jwt = jwt("iss", "trusted");
 
@@ -79,17 +79,34 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 					.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
 			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
 					.setBody(JWK_SET));
+			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
+					.setBody(JWK_SET));
 			JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
 					new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
 			jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
 			JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 					issuer);
-			MockServerWebExchange exchange = withBearerToken(jws.serialize());
-			ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(exchange)
-					.block();
+			ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
 			assertThat(authenticationManager).isNotNull();
-			ReactiveAuthenticationManager cachedAuthenticationManager = authenticationManagerResolver.resolve(exchange)
-					.block();
+			BearerTokenAuthenticationToken token = withBearerToken(jws.serialize());
+			Authentication authentication = authenticationManager.authenticate(token).block();
+			assertThat(authentication).isNotNull();
+			assertThat(authentication.isAuthenticated()).isTrue();
+		}
+	}
+
+	@Test
+	public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
+		try (MockWebServer server = new MockWebServer()) {
+			String issuer = server.url("").toString();
+			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
+					.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
+			server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
+					.setBody(JWK_SET));
+			TrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(
+					(iss) -> iss.equals(issuer));
+			ReactiveAuthenticationManager authenticationManager = resolver.resolve(issuer).block();
+			ReactiveAuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer).block();
 			assertThat(authenticationManager).isSameAs(cachedAuthenticationManager);
 		}
 	}
@@ -98,53 +115,48 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 	public void resolveWhenUsingUntrustedIssuerThenException() {
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				"other", "issuers");
-		MockServerWebExchange exchange = withBearerToken(this.jwt);
+		Authentication token = withBearerToken(this.jwt);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((authenticationManager) -> authenticationManager.authenticate(token))
+						.block())
 				.withMessageContaining("Invalid issuer");
 		// @formatter:on
 	}
 
 	@Test
 	public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() {
+		Authentication token = withBearerToken(this.jwt);
 		ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
+		when(authenticationManager.authenticate(token)).thenReturn(Mono.empty());
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				(issuer) -> Mono.just(authenticationManager));
-		MockServerWebExchange exchange = withBearerToken(this.jwt);
-		assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager);
-	}
-
-	@Test
-	public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverAndCustomServerBearerTokenAuthenticationConverterThenUses() {
-		ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
-		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
-				(issuer) -> Mono.just(authenticationManager));
-		ServerBearerTokenAuthenticationConverter serverBearerTokenAuthenticationConverterSpy = spy(
-				new ServerBearerTokenAuthenticationConverter());
-		authenticationManagerResolver
-				.setServerBearerTokenAuthenticationConverter(serverBearerTokenAuthenticationConverterSpy);
-		MockServerWebExchange exchange = withBearerToken(this.jwt);
-		assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager);
-		verify(serverBearerTokenAuthenticationConverterSpy).convert(any());
+		authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();
+		verify(authenticationManager).authenticate(any());
 	}
 
 	@Test
 	public void resolveWhenUsingExternalSourceThenRespondsToChanges() {
-		MockServerWebExchange exchange = withBearerToken(this.jwt);
+		Authentication token = withBearerToken(this.jwt);
 		Map<String, ReactiveAuthenticationManager> authenticationManagers = new HashMap<>();
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				(issuer) -> Mono.justOrEmpty(authenticationManagers.get(issuer)));
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((manager) -> manager.authenticate(token)).block())
 				.withMessageContaining("Invalid issuer");
 		ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
+		when(authenticationManager.authenticate(token)).thenReturn(Mono.empty());
 		authenticationManagers.put("trusted", authenticationManager);
-		assertThat(authenticationManagerResolver.resolve(exchange).block()).isSameAs(authenticationManager);
+		authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();
+		verify(authenticationManager).authenticate(token);
 		authenticationManagers.clear();
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((manager) -> manager.authenticate(token))
+						.block())
 				.withMessageContaining("Invalid issuer");
 		// @formatter:on
 	}
@@ -153,10 +165,12 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenMalformedThenException() {
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				"trusted");
-		MockServerWebExchange exchange = withBearerToken("jwt");
+		Authentication token = withBearerToken("jwt");
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((manager) -> manager.authenticate(token))
+						.block())
 				.withMessageNotContaining("Invalid issuer");
 		// @formatter:on
 	}
@@ -165,9 +179,10 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenNoIssuerThenException() {
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				"trusted");
-		MockServerWebExchange exchange = withBearerToken(this.noIssuer);
+		Authentication token = withBearerToken(this.noIssuer);
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((manager) -> manager.authenticate(token)).block())
 				.withMessageContaining("Missing issuer");
 	}
 
@@ -175,10 +190,12 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 	public void resolveWhenBearerTokenEvilThenGenericException() {
 		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
 				"trusted");
-		MockServerWebExchange exchange = withBearerToken(this.evil);
+		Authentication token = withBearerToken(this.evil);
 		// @formatter:off
 		assertThatExceptionOfType(OAuth2AuthenticationException.class)
-				.isThrownBy(() -> authenticationManagerResolver.resolve(exchange).block())
+				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
+						.flatMap((manager) -> manager.authenticate(token))
+						.block())
 				.withMessage("Invalid token");
 		// @formatter:on
 	}
@@ -202,13 +219,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
 		return jwt.serialize();
 	}
 
-	private MockServerWebExchange withBearerToken(String token) {
-		// @formatter:off
-		MockServerHttpRequest request = MockServerHttpRequest.get("/")
-				.header("Authorization", "Bearer " + token)
-				.build();
-		// @formatter:on
-		return MockServerWebExchange.from(request);
+	private BearerTokenAuthenticationToken withBearerToken(String token) {
+		return new BearerTokenAuthenticationToken(token);
 	}
 
 }