From 3f74991ce95a5eb47e81108d83b3ea6347ea80aa Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:33:13 -0500 Subject: [PATCH] Authentication adds FactorGrantedAuthority Closes gh-18001 --- .../cas/authentication/CasAuthenticationProvider.java | 4 ++-- .../dao/AbstractUserDetailsAuthenticationProvider.java | 4 ++-- .../AbstractLdapAuthenticationProvider.java | 4 ++-- .../OAuth2LoginAuthenticationProvider.java | 4 ++-- .../authentication/JwtAuthenticationConverter.java | 4 ++-- .../OpaqueTokenAuthenticationProvider.java | 4 ++-- .../OpenSaml5AuthenticationProvider.java | 4 ++-- ...urityMockWithAuthoritiesMvcResultMatchersTests.java | 10 ++++------ .../authentication/WebAuthnAuthenticationProvider.java | 4 ++-- 9 files changed, 20 insertions(+), 22 deletions(-) diff --git a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java index 5464d16993..0ae5b67836 100644 --- a/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java +++ b/cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java @@ -41,7 +41,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; @@ -151,7 +151,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia this.userDetailsChecker.check(userDetails); Collection authorities = new ArrayList<>( this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities())); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); return new CasAuthenticationToken(this.key, userDetails, credentials, authorities, userDetails, assertion); } catch (TicketValidationException ex) { diff --git a/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java b/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java index edb50ff92f..baf82bafdf 100644 --- a/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java +++ b/core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java @@ -39,7 +39,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.userdetails.UserCache; @@ -207,7 +207,7 @@ public abstract class AbstractUserDetailsAuthenticationProvider // authentication events after cache expiry contain the details Collection authorities = new LinkedHashSet<>( this.authoritiesMapper.mapAuthorities(user.getAuthorities())); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal, authentication.getCredentials(), authorities); result.setDetails(authentication.getDetails()); diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java index 11e5310510..6b2b8150c1 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/AbstractLdapAuthenticationProvider.java @@ -35,7 +35,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityMessageSource; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.userdetails.UserDetails; @@ -107,7 +107,7 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati : user.getPassword(); Collection authorities = new LinkedHashSet<>( this.authoritiesMapper.mapAuthorities(user.getAuthorities())); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(user, password, authorities); result.setDetails(authentication.getDetails()); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java index 8d369db4af..c7fce78574 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/OAuth2LoginAuthenticationProvider.java @@ -26,7 +26,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; @@ -127,7 +127,7 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider Collection authorities = new HashSet<>(oauth2User.getAuthorities()); Collection mappedAuthorities = new LinkedHashSet<>( this.authoritiesMapper.mapAuthorities(authorities)); - mappedAuthorities.add(new SimpleGrantedAuthority(AUTHORITY)); + mappedAuthorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken( loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(), oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken()); diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.java index 41b19c1f79..15fbbddc65 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationConverter.java @@ -23,7 +23,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.util.Assert; @@ -46,7 +46,7 @@ public class JwtAuthenticationConverter implements Converter authorities = new HashSet<>(this.jwtGrantedAuthoritiesConverter.convert(jwt)); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); String principalClaimValue = jwt.getClaimAsString(this.principalClaimName); return new JwtAuthenticationToken(jwt, authorities, principalClaimValue); } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java index d8e2fa9bff..cfcbecedf2 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java @@ -30,7 +30,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal; import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames; @@ -155,7 +155,7 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken, iat, exp); Collection authorities = new HashSet<>(authenticatedPrincipal.getAuthorities()); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); return new BearerTokenAuthentication(authenticatedPrincipal, accessToken, authorities); } diff --git a/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProvider.java b/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProvider.java index 953aa71ecf..91ba7c1a61 100644 --- a/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProvider.java +++ b/saml2/saml2-service-provider/src/opensaml5Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml5AuthenticationProvider.java @@ -61,7 +61,7 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; @@ -906,7 +906,7 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor); Collection authorities = new HashSet<>( this.grantedAuthoritiesConverter.convert(assertion)); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId); } diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockWithAuthoritiesMvcResultMatchersTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockWithAuthoritiesMvcResultMatchersTests.java index 64ba0f431c..4d5bd433b4 100644 --- a/test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockWithAuthoritiesMvcResultMatchersTests.java +++ b/test/src/test/java/org/springframework/security/test/web/servlet/response/SecurityMockWithAuthoritiesMvcResultMatchersTests.java @@ -67,12 +67,10 @@ public class SecurityMockWithAuthoritiesMvcResultMatchersTests { } @Test - public void withAuthoritiesNotOrderSensitive() throws Exception { - List grantedAuthorities = new ArrayList<>(); - grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); - grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); - grantedAuthorities.add(new SimpleGrantedAuthority(GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY)); - this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities)); + public void withAuthoritiesStringAllowsAnyOrderAndPermitsAnyImpl() throws Exception { + this.mockMvc.perform(formLogin()) + .andExpect(authenticated().withAuthorities("ROLE_ADMIN", "ROLE_SELLER", + GrantedAuthorities.FACTOR_PASSWORD_AUTHORITY)); } @Test diff --git a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java index 1b89616caf..30e65f713b 100644 --- a/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java +++ b/webauthn/src/main/java/org/springframework/security/web/webauthn/authentication/WebAuthnAuthenticationProvider.java @@ -25,7 +25,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthorities; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.FactorGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; @@ -74,7 +74,7 @@ public class WebAuthnAuthenticationProvider implements AuthenticationProvider { String username = userEntity.getName(); UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); Collection authorities = new HashSet<>(userDetails.getAuthorities()); - authorities.add(new SimpleGrantedAuthority(AUTHORITY)); + authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY)); return new WebAuthnAuthentication(userEntity, authorities); } catch (RuntimeException ex) {