Browse Source

Add SecurityAssertions

This commit introduces a simple, internal test API for
verifying aspects of an Authentication, like its name
and authorities.

Closes gh-17844
Josh Cummings 1 week ago
parent
commit
c64b086878
15 changed files with 180 additions and 108 deletions
  1. 3 5
      config/src/test/java/org/springframework/security/config/annotation/authentication/AuthenticationManagerBuilderTests.java
  2. 21 43
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java
  3. 1 0
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java
  4. 100 0
      core/src/test/java/org/springframework/security/authentication/SecurityAssertions.java
  5. 2 1
      ldap/spring-security-ldap.gradle
  6. 3 2
      ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java
  7. 1 1
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java
  8. 2 0
      oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle
  9. 3 5
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverterTests.java
  10. 10 6
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtBearerTokenAuthenticationConverterTests.java
  11. 2 6
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtReactiveAuthenticationManagerTests.java
  12. 9 5
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProviderTests.java
  13. 9 5
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManagerTests.java
  14. 12 28
      oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverterAdapterTests.java
  15. 2 1
      saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java

+ 3 - 5
config/src/test/java/org/springframework/security/config/annotation/authentication/AuthenticationManagerBuilderTests.java

@@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
 import org.springframework.security.config.ObjectPostProcessor;
@@ -44,7 +45,6 @@ import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.PasswordEncodedUser;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@@ -107,8 +107,7 @@ public class AuthenticationManagerBuilderTests {
 			.getAuthenticationManager();
 		Authentication auth = manager
 			.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
-		assertThat(auth.getName()).isEqualTo("user");
-		assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
+		SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
 	}
 
 	@Test
@@ -119,8 +118,7 @@ public class AuthenticationManagerBuilderTests {
 			.getAuthenticationManager();
 		Authentication auth = manager
 			.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
-		assertThat(auth.getName()).isEqualTo("user");
-		assertThat(auth.getAuthorities()).extracting(GrantedAuthority::getAuthority).containsOnly("ROLE_USER");
+		SecurityAssertions.assertThat(auth).name("user").hasAuthority("ROLE_USER");
 	}
 
 	@Test

+ 21 - 43
config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurerTests.java

@@ -45,6 +45,7 @@ import org.springframework.mock.web.MockFilterChain;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
 import org.springframework.security.config.Customizer;
 import org.springframework.security.config.ObjectPostProcessor;
@@ -217,10 +218,9 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OAuth2UserAuthority.class)
-			.hasToString("OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication)
+			.hasAuthority("OAUTH2_USER")
+			.isInstanceOf(OAuth2UserAuthority.class);
 	}
 
 	@Test
@@ -234,10 +234,9 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OAuth2UserAuthority.class)
-			.hasToString("OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication)
+			.hasAuthority("OAUTH2_USER")
+			.isInstanceOf(OAuth2UserAuthority.class);
 		SecurityContextHolderStrategy strategy = this.context.getBean(SecurityContextHolderStrategy.class);
 		verify(strategy, atLeastOnce()).getDeferredContext();
 		SecurityContextChangedListener listener = this.context.getBean(SecurityContextChangedListener.class);
@@ -255,10 +254,9 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OAuth2UserAuthority.class)
-			.hasToString("OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication)
+			.hasAuthority("OAUTH2_USER")
+			.isInstanceOf(OAuth2UserAuthority.class);
 	}
 
 	// gh-6009
@@ -296,9 +294,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(2);
-		assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
-		assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
 	}
 
 	@Test
@@ -317,9 +313,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(2);
-		assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
-		assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
 	}
 
 	@Test
@@ -338,9 +332,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(2);
-		assertThat(authentication.getAuthorities()).first().hasToString("OAUTH2_USER");
-		assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthorities("OAUTH2_USER", "ROLE_OAUTH2_USER");
 	}
 
 	// gh-5488
@@ -361,10 +353,9 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OAuth2UserAuthority.class)
-			.hasToString("OAUTH2_USER");
+		SecurityAssertions.assertThat(authentication)
+			.hasAuthority("OAUTH2_USER")
+			.isInstanceOf(OAuth2UserAuthority.class);
 	}
 
 	// gh-5521
@@ -570,10 +561,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OidcUserAuthority.class)
-			.hasToString("OIDC_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
 	}
 
 	@Test
@@ -593,9 +581,7 @@ public class OAuth2LoginConfigurerTests {
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
 		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OidcUserAuthority.class)
-			.hasToString("OIDC_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
 	}
 
 	@Test
@@ -614,9 +600,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(2);
-		assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
-		assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
 	}
 
 	@Test
@@ -635,9 +619,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(2);
-		assertThat(authentication.getAuthorities()).first().hasToString("OIDC_USER");
-		assertThat(authentication.getAuthorities()).last().hasToString("ROLE_OIDC_USER");
+		SecurityAssertions.assertThat(authentication).hasAuthorities("OIDC_USER", "ROLE_OIDC_USER");
 	}
 
 	@Test
@@ -690,11 +672,7 @@ public class OAuth2LoginConfigurerTests {
 		Authentication authentication = this.securityContextRepository
 			.loadContext(new HttpRequestResponseHolder(this.request, this.response))
 			.getAuthentication();
-		assertThat(authentication.getAuthorities()).hasSize(1);
-		assertThat(authentication.getAuthorities()).first()
-			.isInstanceOf(OidcUserAuthority.class)
-			.hasToString("OIDC_USER");
-
+		SecurityAssertions.assertThat(authentication).hasAuthority("OIDC_USER").isInstanceOf(OidcUserAuthority.class);
 		// Ensure shared objects set for OAuth2 Client are not used
 		ClientRegistrationRepository clientRegistrationRepository = this.spring.getContext()
 			.getBean(ClientRegistrationRepository.class);

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

@@ -2674,6 +2674,7 @@ public class OAuth2ResourceServerConfigurerTests {
 		String requiresReadScope(JwtAuthenticationToken token) {
 			return token.getAuthorities()
 				.stream()
+				.filter((ga) -> ga.getAuthority().startsWith("SCOPE_"))
 				.map(GrantedAuthority::getAuthority)
 				.collect(Collectors.toList())
 				.toString();

+ 100 - 0
core/src/test/java/org/springframework/security/authentication/SecurityAssertions.java

@@ -0,0 +1,100 @@
+/*
+ * Copyright 2004-present 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.authentication;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.assertj.core.api.AbstractObjectAssert;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.api.CollectionAssert;
+import org.assertj.core.api.Condition;
+import org.assertj.core.api.ObjectAssert;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+
+@NullMarked
+public final class SecurityAssertions {
+
+	private SecurityAssertions() {
+
+	}
+
+	public static AuthenticationAssert assertThat(@Nullable Authentication authentication) {
+		Assertions.assertThat(authentication).isNotNull();
+		return new AuthenticationAssert(authentication);
+	}
+
+	public static final class AuthenticationAssert extends AbstractObjectAssert<AuthenticationAssert, Authentication> {
+
+		private final Authentication authentication;
+
+		private AuthenticationAssert(Authentication authentication) {
+			super(authentication, AuthenticationAssert.class);
+			this.authentication = authentication;
+		}
+
+		public AuthenticationAssert name(String name) {
+			Assertions.assertThat(this.authentication.getName()).isEqualTo(name);
+			return this;
+		}
+
+		public ObjectAssert<GrantedAuthority> hasAuthority(String authority) {
+			Collection<? extends GrantedAuthority> actual = this.authentication.getAuthorities();
+			for (GrantedAuthority element : actual) {
+				if (element.getAuthority().equals(authority)) {
+					return new ObjectAssert<>(element);
+				}
+			}
+			throw new AssertionError(actual + " does not contain " + authority + " as expected");
+		}
+
+		public CollectionAssert<GrantedAuthority> hasAuthorities(String... authorities) {
+			HasAuthoritiesPredicate test = new HasAuthoritiesPredicate(authorities);
+			return authorities().has(new Condition<>(test, "contains %s", Arrays.toString(authorities)));
+		}
+
+		public CollectionAssert<GrantedAuthority> authorities() {
+			return new CollectionAssert<>(this.authentication.getAuthorities());
+		}
+
+	}
+
+	private static final class HasAuthoritiesPredicate implements Predicate<Collection<? extends GrantedAuthority>> {
+
+		private final Collection<String> expected;
+
+		private HasAuthoritiesPredicate(String... expected) {
+			this.expected = List.of(expected);
+		}
+
+		@Override
+		public boolean test(Collection<? extends GrantedAuthority> actual) {
+			Set<String> asString = AuthorityUtils.authorityListToSet(actual);
+			return asString.containsAll(this.expected);
+		}
+
+	}
+
+}

+ 2 - 1
ldap/spring-security-ldap.gradle

@@ -19,7 +19,8 @@ dependencies {
 		exclude(group: 'org.springframework.data', module: 'spring-data-commons')
 	}
 
-	testImplementation project(':spring-security-test')
+	testImplementation project(path : ':spring-security-core', configuration : 'tests')
+	testImplementation project(":spring-security-test")
 	testImplementation 'org.slf4j:slf4j-api'
 	testImplementation "org.assertj:assertj-core"
 	testImplementation "org.junit.jupiter:junit-jupiter-api"

+ 3 - 2
ldap/src/test/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProviderTests.java

@@ -42,6 +42,7 @@ import org.springframework.security.authentication.CredentialsExpiredException;
 import org.springframework.security.authentication.DisabledException;
 import org.springframework.security.authentication.InternalAuthenticationServiceException;
 import org.springframework.security.authentication.LockedException;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider.ContextFactory;
@@ -357,10 +358,10 @@ public class ActiveDirectoryLdapAuthenticationProviderTests {
 			.willReturn(new MockNamingEnumeration(sr));
 		provider.contextFactory = createContextFactoryReturning(this.ctx);
 		Authentication result = provider.authenticate(this.joe);
-		assertThat(result.getAuthorities()).isEmpty();
+		SecurityAssertions.assertThat(result).authorities().doesNotHaveToString("Admin");
 		dca.addAttributeValue("memberOf", "CN=Admin,CN=Users,DC=mydomain,DC=eu");
 		result = provider.authenticate(this.joe);
-		assertThat(result.getAuthorities()).hasSize(1);
+		SecurityAssertions.assertThat(result).hasAuthority("Admin");
 	}
 
 	static class MockNamingEnumeration implements NamingEnumeration<SearchResult> {

+ 1 - 1
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProviderTests.java

@@ -165,7 +165,7 @@ public class OAuth2LoginAuthenticationProviderTests {
 		assertThat(authentication.isAuthenticated()).isTrue();
 		assertThat(authentication.getPrincipal()).isEqualTo(principal);
 		assertThat(authentication.getCredentials()).isEqualTo("");
-		assertThat(authentication.getAuthorities()).isEqualTo(authorities);
+		assertThat(authentication.getAuthorities()).containsAll(authorities);
 		assertThat(authentication.getClientRegistration()).isEqualTo(this.clientRegistration);
 		assertThat(authentication.getAuthorizationExchange()).isEqualTo(this.authorizationExchange);
 		assertThat(authentication.getAccessToken()).isEqualTo(accessTokenResponse.getAccessToken());

+ 2 - 0
oauth2/oauth2-resource-server/spring-security-oauth2-resource-server.gradle

@@ -14,6 +14,7 @@ dependencies {
 
 	provided 'jakarta.servlet:jakarta.servlet-api'
 
+	testImplementation project(path : ':spring-security-core', configuration : 'tests')
 	testImplementation project(path: ':spring-security-oauth2-jose', configuration: 'tests')
 	testImplementation 'com.squareup.okhttp3:mockwebserver'
 	testImplementation 'com.fasterxml.jackson.core:jackson-databind'
@@ -27,5 +28,6 @@ dependencies {
 	testImplementation "org.mockito:mockito-junit-jupiter"
 	testImplementation "org.springframework:spring-test"
 
+
 	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
 }

+ 3 - 5
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverterTests.java

@@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.oauth2.jwt.Jwt;
@@ -46,9 +47,7 @@ public class JwtAuthenticationConverterTests {
 	public void convertWhenDefaultGrantedAuthoritiesConverterSet() {
 		Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		assertThat(authorities).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
-				new SimpleGrantedAuthority("SCOPE_message:write"));
+		SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
 	}
 
 	@Test
@@ -65,8 +64,7 @@ public class JwtAuthenticationConverterTests {
 			.asList(new SimpleGrantedAuthority("blah"));
 		this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		assertThat(authorities).containsExactly(new SimpleGrantedAuthority("blah"));
+		SecurityAssertions.assertThat(authentication).hasAuthority("blah");
 	}
 
 	@Test

+ 10 - 6
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtBearerTokenAuthenticationConverterTests.java

@@ -17,11 +17,13 @@
 package org.springframework.security.oauth2.server.resource.authentication;
 
 import java.util.Arrays;
+import java.util.function.Predicate;
 
 import org.junit.jupiter.api.Test;
 
 import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.authentication.SecurityAssertions;
+import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.oauth2.jwt.Jwt;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +50,7 @@ public class JwtBearerTokenAuthenticationConverterTests {
 		BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
 		assertThat(bearerToken.getToken().getTokenValue()).isEqualTo("token-value");
 		assertThat(bearerToken.getTokenAttributes()).containsOnlyKeys("claim");
-		assertThat(bearerToken.getAuthorities()).isEmpty();
+		assertThat(bearerToken.getAuthorities()).noneMatch(isScope());
 	}
 
 	@Test
@@ -62,8 +64,7 @@ public class JwtBearerTokenAuthenticationConverterTests {
 		AbstractAuthenticationToken token = this.converter.convert(jwt);
 		assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
 		BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
-		assertThat(bearerToken.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
-				new SimpleGrantedAuthority("SCOPE_message:write"));
+		SecurityAssertions.assertThat(bearerToken).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
 	}
 
 	@Test
@@ -77,8 +78,11 @@ public class JwtBearerTokenAuthenticationConverterTests {
 		AbstractAuthenticationToken token = this.converter.convert(jwt);
 		assertThat(token).isInstanceOf(BearerTokenAuthentication.class);
 		BearerTokenAuthentication bearerToken = (BearerTokenAuthentication) token;
-		assertThat(bearerToken.getAuthorities()).containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
-				new SimpleGrantedAuthority("SCOPE_message:write"));
+		SecurityAssertions.assertThat(bearerToken).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
+	}
+
+	static Predicate<GrantedAuthority> isScope() {
+		return (a) -> a.getAuthority().startsWith("SCOPE_");
 	}
 
 }

+ 2 - 6
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtReactiveAuthenticationManagerTests.java

@@ -23,10 +23,10 @@ import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import reactor.core.publisher.Mono;
 
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
 import org.springframework.security.oauth2.jwt.BadJwtException;
 import org.springframework.security.oauth2.jwt.Jwt;
@@ -137,11 +137,7 @@ public class JwtReactiveAuthenticationManagerTests {
 		Authentication authentication = this.manager.authenticate(token).block();
 		assertThat(authentication).isNotNull();
 		assertThat(authentication.isAuthenticated()).isTrue();
-		// @formatter:off
-		assertThat(authentication.getAuthorities())
-				.extracting(GrantedAuthority::getAuthority)
-				.containsOnly("SCOPE_message:read", "SCOPE_message:write");
-		// @formatter:on
+		SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
 	}
 
 }

+ 9 - 5
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProviderTests.java

@@ -21,12 +21,15 @@ import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
+import java.util.function.Predicate;
 
 import org.junit.jupiter.api.Test;
 
 import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
 import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
@@ -75,10 +78,7 @@ public class OpaqueTokenAuthenticationProviderTests {
 				.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
 				.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
 				.containsEntry("extension_field", "twenty-seven");
-		assertThat(result.getAuthorities())
-				.extracting("authority")
-				.containsExactly("SCOPE_read", "SCOPE_write",
-				"SCOPE_dolphin");
+		SecurityAssertions.assertThat(result).hasAuthorities("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
 		// @formatter:on
 	}
 
@@ -97,7 +97,7 @@ public class OpaqueTokenAuthenticationProviderTests {
 				.isNotNull()
 				.doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
 		// @formatter:on
-		assertThat(result.getAuthorities()).isEmpty();
+		SecurityAssertions.assertThat(result).authorities().noneMatch(isScope());
 	}
 
 	@Test
@@ -146,4 +146,8 @@ public class OpaqueTokenAuthenticationProviderTests {
 		verifyNoMoreInteractions(introspector, authenticationConverter);
 	}
 
+	static Predicate<GrantedAuthority> isScope() {
+		return (a) -> a.getAuthority().startsWith("SCOPE_");
+	}
+
 }

+ 9 - 5
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenReactiveAuthenticationManagerTests.java

@@ -21,13 +21,16 @@ import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
+import java.util.function.Predicate;
 
 import org.junit.jupiter.api.Test;
 import reactor.core.publisher.Mono;
 
 import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
 import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
 import org.springframework.security.oauth2.core.TestOAuth2AuthenticatedPrincipals;
@@ -76,10 +79,7 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
 				.containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "Z5O3upPC88QrAjx00dis")
 				.containsEntry(OAuth2TokenIntrospectionClaimNames.USERNAME, "jdoe")
 				.containsEntry("extension_field", "twenty-seven");
-		assertThat(result.getAuthorities())
-				.extracting("authority")
-				.containsExactly("SCOPE_read", "SCOPE_write",
-				"SCOPE_dolphin");
+		SecurityAssertions.assertThat(result).hasAuthorities("SCOPE_read", "SCOPE_write", "SCOPE_dolphin");
 		// @formatter:on
 	}
 
@@ -94,7 +94,7 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
 		assertThat(result.getPrincipal()).isInstanceOf(OAuth2IntrospectionAuthenticatedPrincipal.class);
 		Map<String, Object> attributes = ((OAuth2AuthenticatedPrincipal) result.getPrincipal()).getAttributes();
 		assertThat(attributes).isNotNull().doesNotContainKey(OAuth2TokenIntrospectionClaimNames.SCOPE);
-		assertThat(result.getAuthorities()).isEmpty();
+		SecurityAssertions.assertThat(result).authorities().noneMatch(isScope());
 	}
 
 	@Test
@@ -145,4 +145,8 @@ public class OpaqueTokenReactiveAuthenticationManagerTests {
 		verifyNoMoreInteractions(introspector, authenticationConverter);
 	}
 
+	static Predicate<GrantedAuthority> isScope() {
+		return (a) -> a.getAuthority().startsWith("SCOPE_");
+	}
+
 }

+ 12 - 28
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/ReactiveJwtAuthenticationConverterAdapterTests.java

@@ -17,19 +17,17 @@
 package org.springframework.security.oauth2.server.resource.authentication;
 
 import java.util.Arrays;
-import java.util.Collection;
+import java.util.function.Predicate;
 
 import org.junit.jupiter.api.Test;
 
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.oauth2.jwt.Jwt;
 import org.springframework.security.oauth2.jwt.TestJwts;
 
-import static org.assertj.core.api.Assertions.assertThat;
-
 /**
  * Tests for {@link ReactiveJwtAuthenticationConverterAdapter}
  *
@@ -46,40 +44,28 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
 	public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
 		Jwt jwt = TestJwts.jwt().claim("scope", "message:read message:write").build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		// @formatter:off
-		assertThat(authorities)
-				.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
-						new SimpleGrantedAuthority("SCOPE_message:write"));
-		// @formatter:on
+		SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
 	}
 
 	@Test
 	public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
 		Jwt jwt = TestJwts.jwt().claim("scope", "").build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		assertThat(authorities).containsExactly();
+		SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
 	}
 
 	@Test
 	public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
 		Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList("message:read", "message:write")).build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		// @formatter:off
-		assertThat(authorities)
-				.containsExactly(new SimpleGrantedAuthority("SCOPE_message:read"),
-						new SimpleGrantedAuthority("SCOPE_message:write"));
-		// @formatter:on
+		SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_message:read", "SCOPE_message:write");
 	}
 
 	@Test
 	public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
 		Jwt jwt = TestJwts.jwt().claim("scp", Arrays.asList()).build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		assertThat(authorities).containsExactly();
+		SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
 	}
 
 	@Test
@@ -89,12 +75,7 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
 			.claim("scope", "missive:read missive:write")
 			.build();
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		// @formatter:off
-		assertThat(authorities)
-				.containsExactly(new SimpleGrantedAuthority("SCOPE_missive:read"),
-						new SimpleGrantedAuthority("SCOPE_missive:write"));
-		// @formatter:on
+		SecurityAssertions.assertThat(authentication).hasAuthorities("SCOPE_missive:read", "SCOPE_missive:write");
 	}
 
 	@Test
@@ -106,8 +87,11 @@ public class ReactiveJwtAuthenticationConverterAdapterTests {
 				.build();
 		// @formatter:on
 		AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
-		Collection<GrantedAuthority> authorities = authentication.getAuthorities();
-		assertThat(authorities).containsExactly();
+		SecurityAssertions.assertThat(authentication).authorities().noneMatch(isScope());
+	}
+
+	static Predicate<GrantedAuthority> isScope() {
+		return (a) -> a.getAuthority().startsWith("SCOPE_");
 	}
 
 }

+ 2 - 1
saml2/saml2-service-provider/src/opensaml5Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProviderTests.java

@@ -71,6 +71,7 @@ import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;
 import org.opensaml.xmlsec.signature.support.SignatureConstants;
 
 import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.authentication.SecurityAssertions;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
@@ -734,7 +735,7 @@ public class OpenSaml5AuthenticationProviderTests {
 		Response response = TestOpenSamlObjects.signedResponseWithOneAssertion();
 		Saml2AuthenticationToken token = token(response, verifying(registration()));
 		Authentication authentication = provider.authenticate(token);
-		assertThat(AuthorityUtils.authorityListToSet(authentication.getAuthorities())).containsExactly("CUSTOM");
+		SecurityAssertions.assertThat(authentication).hasAuthority("CUSTOM");
 		verify(grantedAuthoritiesConverter).convert(any());
 	}