Let's get started with a Microservice Architecture with Spring Cloud:
Multi-Factor Authentication in Spring Security 7
Last updated: May 27, 2026
1. Overview
Authentication is the first line of defense in all web applications. Traditionally, applications rely on a username and password to verify a user’s identity. However, experts no longer consider relying on a single authentication factor secure for modern systems.
Multi-Factor Authentication (MFA) addresses this by requiring users to verify their identity using multiple independent factors before accessing a system.
Spring Security 7 introduces built-in support for multi-factor authentication, allowing developers to enforce multiple authentication steps using the existing authorization model. In this article, we’ll explore how MFA works in Spring Security 7 and how to implement it in a Spring Boot application.
2. Understanding MFA in Spring Security 7
Spring Security 7 introduces a new way to model authentication factors using authorities. Instead of treating MFA as a separate authentication mechanism, Spring Security progressively grants authorities for each successful authentication factor.
Multi-Factor Authentication (MFA) strengthens security by requiring users to verify their identity using multiple independent factors. These factors typically fall into three categories: something the user knows, such as a password or PIN, something the user has, such as a mobile device or email token, and something the user is, such as a fingerprint or other biometric data. By combining these factors, applications significantly reduce the risk of compromised credentials and unauthorized access.
Each time a user successfully authenticates with a specific factor, Spring Security adds a corresponding authority to the authentication object. This authority represents the factor that has already been verified during the authentication process. Some common examples include FACTOR_PASSWORD for password-based authentication, FACTOR_X509 for certificate-based authentication, and FACTOR_OTT for one-time token authentication. These authorities are represented internally by the FactorGrantedAuthority class and become part of the authenticated user’s security context.
This design allows authorization rules to verify that the required authentication factors are satisfied before granting access to protected resources.
3. Project Setup
Before implementing multi-factor authentication, we’ll set up a simple Spring Boot application using Spring Initializr with Spring Security 7. First, we need to configure the required dependencies.
We include Spring Boot starters for web, security, starter-test, webmvc-test, and security-testing:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>4.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<version>4.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>7.0.0</version>
<scope>test</scope>
</dependency>
This configuration adds the core dependencies required for our application. It includes Spring Boot’s web support for building REST endpoints, the Spring Security framework for implementing authentication and authorization, and Spring Security testing utilities for writing security-focused unit tests.
With these dependencies in place, the application now has everything required to configure and enable multi-factor authentication.
4. Enabling Multi-Factor Authentication Globally
Spring Security 7 introduces a convenient annotation called @EnableMultiFactorAuthentication. This annotation allows developers to define which authentication factors are required for all protected endpoints.
The following configuration enables MFA globally using password and X509 authentication factors:
@Configuration
@EnableWebSecurity
@EnableMultiFactorAuthentication(
authorities = { FactorGrantedAuthority.PASSWORD_AUTHORITY, FactorGrantedAuthority.X509_AUTHORITY }
)
public class GlobalMfaSecurityConfig {
@Bean
@Order(3)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher("/**").authorizeHttpRequests(auth ->auth.requestMatchers("/public")
.permitAll().anyRequest().authenticated()).formLogin(withDefaults());
return http.build();
}
}
This configuration enables Spring Security for the application and enables multi-factor authentication globally. As part of the setup, it specifies that the user must complete two authentication factors before the system considers them fully authenticated.
In this case, the required factors are password and X.509 certificate authentication. Once the configuration is active, every secured endpoint in the application requires both factors for access to be granted. Applying MFA globally like this helps avoid inconsistencies and reduces the risk of missing MFA rules in individual authorization configurations.
5. Applying MFA to Specific Endpoints
In many applications, MFA should be required only for sensitive endpoints, such as account settings, financial operations, or admin actions. Spring Security provides the AuthorizationManagerFactory API to define MFA rules programmatically.
The following configuration requires MFA only for admin endpoints:
@Configuration
@EnableWebSecurity
public class AdminMfaSecurityConfig {
@Bean
@Order(1)
SecurityFilterChain adminSecurityFilterChain(HttpSecurity http) throws Exception {
AuthorizationManagerFactory<Object> mfa = AuthorizationManagerFactories.multiFactor()
.requireFactors(
FactorGrantedAuthority.PASSWORD_AUTHORITY,
FactorGrantedAuthority.X509_AUTHORITY)
.build();
http.securityMatcher("/admin/**").authorizeHttpRequests(auth ->auth.requestMatchers("/admin/**")
.access(mfa.hasRole("ADMIN")).anyRequest().authenticated()).formLogin(withDefaults());
return http.build();
}
}
In this configuration, multi-factor authentication is applied only to requests that match the /admin/** endpoints. When a user attempts to access these routes, Spring Security checks whether the required authentication factors are present.
In addition to completing both authentication factors, the user must also have the ADMIN role. This approach provides fine-grained control over security rules, allowing developers to enforce MFA only on sensitive endpoints while keeping the rest of the application accessible with standard authentication.
6. Implementing Time-Based Authentication Rules
Some applications require users to re-authenticate before performing sensitive actions. For example, a banking application may require a fresh login before updating payment information. Spring Security 7 supports time-based MFA rules.
The following configuration requires the user to authenticate the password factor within the last five minutes:
@Configuration
@EnableWebSecurity
public class TimeBasedMfaSecurityConfig {
@Bean
@Order(2)
SecurityFilterChain profileSecurityFilterChain(HttpSecurity http) throws Exception {
AuthorizationManagerFactory<Object> recentLogin = AuthorizationManagerFactories.
multiFactor().requireFactor(
factor -> factor.passwordAuthority().validDuration(Duration.ofMinutes(5)))
.build();
http.securityMatcher("/profile", "/profile/**").authorizeHttpRequests(
auth -> auth.requestMatchers("/profile", "/profile/**")
.access(recentLogin.authenticated())
.anyRequest().
authenticated()).formLogin(withDefaults());
return http.build();
}
}
This rule allows users to navigate most parts of the application using their existing authenticated session. However, when a user attempts to access endpoints under */profile/**, the system verifies whether the password authentication was completed recently.
If the password factor is older than five minutes, Spring Security requires the user to authenticate again before granting access. This approach commonly allows users to perform sensitive operations, ensuring they complete critical actions only after a recent, verified authentication step.
7. Implementing User-Based MFA Rules
Sometimes MFA rules apply only to specific users. For example, administrators may be required to use MFA while regular users can log in with a single factor. Spring Security allows implementing custom authorization managers to support such scenarios.
The following example enforces MFA only for the admin user:
@Component
public class AdminMfaAuthorizationManager implements AuthorizationManager<Object> {
AuthorizationManager<Object> mfa =
AllAuthoritiesAuthorizationManager.hasAllAuthorities(FactorGrantedAuthority.OTT_AUTHORITY,
FactorGrantedAuthority.PASSWORD_AUTHORITY);
@Override
public AuthorizationResult authorize(Supplier<? extends Authentication> authentication, Object context) {
Authentication auth = authentication.get();
if (auth != null && "admin".equals(auth.getName())) {
return mfa.authorize(authentication, context);
}
return new AuthorizationDecision(true);
}
}
This logic checks the authenticated user’s identity and applies MFA rules conditionally. If the authenticated user is an admin, the system verifies that the user has completed both required authentication factors before granting access.
For all other users, the system allows the request without enforcing additional MFA checks. This approach is useful when introducing MFA gradually, allowing organizations to enforce stronger security for high-privilege users first before extending it to the entire user base.
8. Writing Unit Tests for MFA
Testing authentication flows is essential to ensure that security rules behave as expected. Without proper tests, it becomes difficult to verify whether authentication requirements such as roles, permissions, or MFA factors are correctly enforced.
Spring Security provides dedicated testing utilities that make it easier to simulate authenticated users and validate authorization behavior. These tools allow developers to write focused tests that verify that security configurations function correctly without requiring a full authentication setup.
8.1. Controller Example
Before writing tests, we define a simple controller that exposes the endpoints secured by our MFA configurations:
@RestController
public class DemoController {
@GetMapping("/public")
public String publicEndpoint() {
return "public endpoint";
}
@GetMapping("/profile")
public String profileEndpoint() {
return "profile endpoint";
}
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "admin dashboard";
}
}
This controller exposes three endpoints /public, /profile, and /admin/dashboard that help demonstrate different MFA enforcement strategies.
8.2. MFA Security Test
This test verifies how the system behaves when it enforces multi-factor authentication globally:
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class GlobalMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenUserWithoutMfa_whenAccessProfile_thenForbidden() throws Exception {
mockMvc.perform(get("/profile").with(user("user").roles("USER")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
This test simulates a user attempting to access the admin endpoint without completing the required MFA factors. Since the authentication request only contains the basic role and does not include the necessary factor authorities, the request does not satisfy the configured security rules. As a result, Spring Security returns a 403 Forbidden response because the user has not completed all required authentication factors.
8.3. Admin Endpoint MFA Test
Next, we’ll verify that the system enforces MFA for admin endpoints:
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class AdminMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenAdminWithoutMfa_whenAccessAdminEndpoint_thenForbidden() throws Exception {
mockMvc.perform(get("/admin/dashboard").with(user("admin").roles("ADMIN")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
This test simulates an administrator attempting to access the /admin/dashboard endpoint. Although the user has the required ADMIN role, the request does not include the required MFA authorities. Because the authentication factors are incomplete, Spring Security redirects the request to the login page. This confirms that MFA enforcement works correctly for privileged endpoints.
8.4. Time-Based MFA Security Test
Some operations require users to authenticate recently to ensure no one has compromised the session. The following test verifies that the profile endpoint requires a recent authentication:
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
class TimeBasedMfaSecurityTest {
@Autowired
MockMvc mockMvc;
@Test
void givenUserWithoutRecentAuthentication_whenAccessProfile_thenForbidden() throws Exception {
mockMvc.perform(get("/profile").with(user("user").roles("USER")))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", containsString("/login")));
}
}
This test checks that the /profile endpoint requires a recent authentication. Since the simulated request does not include the necessary MFA factor information, Spring Security redirects the request to the login page. This confirms that the system correctly enforces the time-based MFA rule.
9. Conclusion
Multi-Factor Authentication (MFA) is a critical security measure for modern applications, helping reduce the risk of unauthorized access by requiring multiple verification factors.
Spring Security 7 provides built-in support for MFA, allowing developers to enforce multiple authentication steps using the existing authorization model. In this article, we explored how FactorGrantedAuthority models MFA, how to enable it globally with @EnableMultiFactorAuthentication, and how to apply it selectively to specific endpoints using AuthorizationManagerFactory. We also covered time-based rules, custom user-based policies, and testing MFA behavior using Spring Security test utilities.
By leveraging these features, developers can build secure and flexible authentication flows. As security threats continue to evolve, implementing MFA is no longer optional but an essential step in protecting user data and critical systems. As always, the code for this example is available over on GitHub.

















