Add Factory Authority When Authentication Succeeds

Issue gh-17933
This commit is contained in:
Josh Cummings 2025-09-19 09:26:41 -06:00
parent 9eaadcc70d
commit e8accd0499
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
10 changed files with 102 additions and 14 deletions

View File

@ -17,14 +17,18 @@
package org.springframework.security.config.annotation.web.configurers; package org.springframework.security.config.annotation.web.configurers;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.Nullable;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer; import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper; import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
@ -37,6 +41,7 @@ import org.springframework.security.web.authentication.preauth.x509.SubjectDnX50
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter; import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor; import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
/** /**
* Adds X509 based pre authentication to an application. Since validating the certificate * Adds X509 based pre authentication to an application. Since validating the certificate
@ -177,8 +182,12 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
public void init(H http) { public void init(H http) {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider(); PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http)); authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
http.authenticationProvider(authenticationProvider) http.authenticationProvider(new AuthorityGrantingAuthenticationProvider(authenticationProvider))
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint()); .setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptions != null) {
exceptions.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE);
}
} }
@Override @Override
@ -225,4 +234,31 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
return context.getBeanProvider(type).getIfUnique(); return context.getBeanProvider(type).getIfUnique();
} }
private static final class AuthorityGrantingAuthenticationProvider implements AuthenticationProvider {
private final AuthenticationProvider delegate;
private AuthorityGrantingAuthenticationProvider(AuthenticationProvider delegate) {
this.delegate = delegate;
}
@Override
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
Authentication result = this.delegate.authenticate(authentication);
if (result == null) {
return result;
}
return result
.toBuilder()
.authorities((a) -> a.add(new SimpleGrantedAuthority("FACTOR_X509")))
.build();
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
} }

View File

@ -34,6 +34,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
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.SpringSecurityMessageSource; import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserCache; import org.springframework.security.core.userdetails.UserCache;
@ -94,6 +95,8 @@ public abstract class AbstractUserDetailsAuthenticationProvider
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
private static final String AUTHORITY = "FACTOR_PASSWORD";
/** /**
* Allows subclasses to perform any additional checks of a returned (or cached) * Allows subclasses to perform any additional checks of a returned (or cached)
* <code>UserDetails</code> for a given authentication request. Generally a subclass * <code>UserDetails</code> for a given authentication request. Generally a subclass
@ -197,8 +200,12 @@ public abstract class AbstractUserDetailsAuthenticationProvider
// so subsequent attempts are successful even with encoded passwords. // so subsequent attempts are successful even with encoded passwords.
// Also ensure we return the original getDetails(), so that future // Also ensure we return the original getDetails(), so that future
// authentication events after cache expiry contain the details // authentication events after cache expiry contain the details
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal, UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken
authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities())); .authenticated(principal, authentication.getCredentials(),
this.authoritiesMapper.mapAuthorities(user.getAuthorities()))
.toBuilder()
.authorities((a) -> a.add(new SimpleGrantedAuthority(AUTHORITY)))
.build();
result.setDetails(authentication.getDetails()); result.setDetails(authentication.getDetails());
this.logger.debug("Authenticated user"); this.logger.debug("Authenticated user");
return result; return result;

View File

@ -16,10 +16,15 @@
package org.springframework.security.authentication.ott; package org.springframework.security.authentication.ott;
import java.util.Collection;
import java.util.HashSet;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
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.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
@ -35,6 +40,8 @@ import org.springframework.util.Assert;
*/ */
public final class OneTimeTokenAuthenticationProvider implements AuthenticationProvider { public final class OneTimeTokenAuthenticationProvider implements AuthenticationProvider {
private static final String AUTHORITY = "FACTOR_OTT";
private final OneTimeTokenService oneTimeTokenService; private final OneTimeTokenService oneTimeTokenService;
private final UserDetailsService userDetailsService; private final UserDetailsService userDetailsService;
@ -56,7 +63,9 @@ public final class OneTimeTokenAuthenticationProvider implements AuthenticationP
} }
try { try {
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername()); UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, user.getAuthorities()); Collection<GrantedAuthority> authorities = new HashSet<>(user.getAuthorities());
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, authorities);
authenticated.setDetails(otpAuthenticationToken.getDetails()); authenticated.setDetails(otpAuthenticationToken.getDetails());
return authenticated; return authenticated;
} }

View File

@ -33,6 +33,7 @@ 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.SpringSecurityMessageSource; import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
@ -50,6 +51,8 @@ import org.springframework.util.StringUtils;
*/ */
public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware { public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
private static final String AUTHORITY = "FACTOR_PASSWORD";
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
@ -100,8 +103,11 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati
UserDetails user) { UserDetails user) {
Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials() Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials()
: user.getPassword(); : user.getPassword();
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(user, password, UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken
this.authoritiesMapper.mapAuthorities(user.getAuthorities())); .authenticated(user, password, this.authoritiesMapper.mapAuthorities(user.getAuthorities()))
.toBuilder()
.authorities((a) -> a.add(new SimpleGrantedAuthority(AUTHORITY)))
.build();
result.setDetails(authentication.getDetails()); result.setDetails(authentication.getDetails());
this.logger.debug("Authenticated user"); this.logger.debug("Authenticated user");
return result; return result;

View File

@ -17,12 +17,14 @@
package org.springframework.security.oauth2.client.authentication; package org.springframework.security.oauth2.client.authentication;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import org.springframework.security.authentication.AuthenticationProvider; 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.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
@ -66,6 +68,8 @@ import org.springframework.util.Assert;
*/ */
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider { public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {
private static final String AUTHORITY = "FACTOR_AUTHORIZATION_CODE";
private final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider; private final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider;
private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService; private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
@ -118,8 +122,9 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters(); Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest( OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters)); loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper Collection<GrantedAuthority> authorities = new HashSet<>(oauth2User.getAuthorities());
.mapAuthorities(oauth2User.getAuthorities()); authorities.add(new SimpleGrantedAuthority(AUTHORITY));
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper.mapAuthorities(authorities);
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken( OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(), loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken()); oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());

View File

@ -17,10 +17,12 @@
package org.springframework.security.oauth2.server.resource.authentication; package org.springframework.security.oauth2.server.resource.authentication;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; 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.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames; import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -34,14 +36,16 @@ import org.springframework.util.Assert;
*/ */
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> { public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private static final String AUTHORITY = "FACTOR_BEARER";
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
private String principalClaimName = JwtClaimNames.SUB; private String principalClaimName = JwtClaimNames.SUB;
@Override @Override
public final AbstractAuthenticationToken convert(Jwt jwt) { public final AbstractAuthenticationToken convert(Jwt jwt) {
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); Collection<GrantedAuthority> authorities = new HashSet<>(this.jwtGrantedAuthoritiesConverter.convert(jwt));
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
String principalClaimValue = jwt.getClaimAsString(this.principalClaimName); String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
return new JwtAuthenticationToken(jwt, authorities, principalClaimValue); return new JwtAuthenticationToken(jwt, authorities, principalClaimValue);
} }

View File

@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.resource.authentication;
import java.time.Instant; import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -28,6 +29,7 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio
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.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames; import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
@ -72,6 +74,8 @@ import org.springframework.util.Assert;
*/ */
public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider { public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
private static final String AUTHORITY = "FACTOR_BEARER";
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());
private final OpaqueTokenIntrospector introspector; private final OpaqueTokenIntrospector introspector;
@ -149,8 +153,9 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
Instant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP); Instant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken, OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken,
iat, exp); iat, exp);
return new BearerTokenAuthentication(authenticatedPrincipal, accessToken, Collection<GrantedAuthority> authorities = new HashSet<>(authenticatedPrincipal.getAuthorities());
authenticatedPrincipal.getAuthorities()); authorities.add(new SimpleGrantedAuthority(AUTHORITY));
return new BearerTokenAuthentication(authenticatedPrincipal, accessToken, authorities);
} }
/** /**

View File

@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -59,6 +60,7 @@ 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.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.saml2.core.Saml2Error; import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2ErrorCodes;
import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; import org.springframework.security.saml2.core.Saml2ResponseValidatorResult;
@ -111,6 +113,8 @@ import org.springframework.util.StringUtils;
*/ */
public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider { public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider {
private static final String AUTHORITY = "FACTOR_SAML_RESPONSE";
private final BaseOpenSamlAuthenticationProvider delegate; private final BaseOpenSamlAuthenticationProvider delegate;
/** /**
@ -899,7 +903,9 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
.attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion)) .attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion))
.build(); .build();
Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor); Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor);
Collection<GrantedAuthority> authorities = this.grantedAuthoritiesConverter.convert(assertion); Collection<GrantedAuthority> authorities = new HashSet<>(
this.grantedAuthoritiesConverter.convert(assertion));
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId); return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId);
} }

View File

@ -67,6 +67,7 @@ public class SecurityMockWithAuthoritiesMvcResultMatchersTests {
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>(); List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER")); grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
grantedAuthorities.add(new SimpleGrantedAuthority("FACTOR_PASSWORD"));
this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities)); this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities));
} }

View File

@ -16,10 +16,15 @@
package org.springframework.security.web.webauthn.authentication; package org.springframework.security.web.webauthn.authentication;
import java.util.Collection;
import java.util.HashSet;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
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.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity; import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
@ -39,6 +44,8 @@ import org.springframework.util.Assert;
*/ */
public class WebAuthnAuthenticationProvider implements AuthenticationProvider { public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
private static final String AUTHORITY = "FACTOR_WEBAUTHN";
private final WebAuthnRelyingPartyOperations relyingPartyOperations; private final WebAuthnRelyingPartyOperations relyingPartyOperations;
private final UserDetailsService userDetailsService; private final UserDetailsService userDetailsService;
@ -65,7 +72,9 @@ public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
.authenticate(webAuthnRequest.getWebAuthnRequest()); .authenticate(webAuthnRequest.getWebAuthnRequest());
String username = userEntity.getName(); String username = userEntity.getName();
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
return new WebAuthnAuthentication(userEntity, userDetails.getAuthorities()); Collection<GrantedAuthority> authorities = new HashSet<>(userDetails.getAuthorities());
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
return new WebAuthnAuthentication(userEntity, authorities);
} }
catch (RuntimeException ex) { catch (RuntimeException ex) {
throw new BadCredentialsException(ex.getMessage(), ex); throw new BadCredentialsException(ex.getMessage(), ex);