Implement Multi-Factor Authentication in Spring Boot 3 with One‑Time Tokens

This guide explains how to add MFA to a Spring Boot 3 application using password plus one‑time token authentication, covering the theory of MFA factors, required dependencies, API definitions, security configuration, token‑generation handling, custom login pages, and the complete verification flow with code snippets and screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Implement Multi-Factor Authentication in Spring Boot 3 with One‑Time Tokens

Introduction

Multi‑Factor Authentication (MFA) requires more than one credential type. The common factors in web applications are a password (something you know) and a one‑time token (something you have).

Demo Setup

Environment

Spring Boot 3.5.0 with spring-boot-starter-security dependency.

API Endpoints

@RestController
public class HomeController {
    @GetMapping("/admin")
    public String admin() { return "Admin Page"; }

    @GetMapping("/ott/sent")
    String ottSent() { return "OneTimeToken Sent"; }
}

The /admin endpoint is protected; /ott/sent serves as a post‑token‑generation confirmation page.

Security Configuration (MFA)

@Configuration
@EnableWebSecurity
@EnableMultiFactorAuthentication(authorities = {
        FactorGrantedAuthority.PASSWORD_AUTHORITY,
        FactorGrantedAuthority.OTT_AUTHORITY })
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/", "/ott/sent", "/login.html", "/ott.html",
                                         "/css/**", "/error").permitAll()
                        .requestMatchers("/admin/**").hasRole("ADMIN"))
                .formLogin(form -> form
                        .loginPage("/login.html")
                        .loginProcessingUrl("/login"))
                .oneTimeTokenLogin(ott -> ott
                        .loginPage("/ott.html")
                        .loginProcessingUrl("/ott/generate"))
                .build();
    }

    @Bean
    UserDetailsService userDetailsService() {
        var user = User.withUsername("user")
                .password("{noop}666666")
                .roles("USER")
                .build();
        var admin = User.withUsername("admin")
                .password("{noop}888888")
                .roles("ADMIN", "USER")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }
}

The @EnableMultiFactorAuthentication annotation adds both password and OTT authorities to every authorization rule.

One‑Time Token Generation Success Handler

@Component
public class OttSuccessHandler implements OneTimeTokenGenerationSuccessHandler {

    private final OneTimeTokenGenerationSuccessHandler redirectHandler =
            new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       OneTimeToken oneTimeToken) throws IOException, ServletException {
        String link = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/login/ott")
                .queryParam("token", oneTimeToken.getTokenValue())
                .toUriString();
        // In production send the link via email, SMS, etc.
        System.out.println("Jump link: %s".formatted(link));
        redirectHandler.handle(request, response, oneTimeToken);
    }
}

The handler builds a URL containing the generated token and prints it to the console.

Custom Authentication Pages

login.html – username/password login page:

<div class="container mt-5">
  <div class="row justify-content-center">
    <div class="col-md-6">
      <div class="card">
        <div class="card-body">
          <h3 class="card-title text-center mb-4">Please Login</h3>
          <form method="post" action="/login">
            <div class="mb-3">
              <label for="username" class="form-label">Username</label>
              <input type="text" class="form-control" id="username" name="username" placeholder="Enter username">
            </div>
            <div class="mb-3">
              <label for="password" class="form-label">Password</label>
              <input type="password" class="form-control" id="password" name="password" placeholder="Enter password">
            </div>
            <button type="submit" class="btn btn-primary w-100">Login</button>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>

ott.html – one‑time token request page:

<div class="container mt-5">
  <div class="row justify-content-center">
    <div class="col-md-6">
      <div class="card">
        <div class="card-body">
          <h3 class="card-title text-center mb-4">Request One‑Time Token</h3>
          <form method="post" action="/ott/generate">
            <div class="mb-3">
              <label for="otp-username" class="form-label">Username</label>
              <input type="text" class="form-control" id="otp-username" name="username" placeholder="Enter username">
            </div>
            <button type="submit" class="btn btn-primary w-100">Send Token</button>
          </form>
        </div>
      </div>
    </div>
  </div>
</div>

Verification Flow

Client requests /admin → Spring Security redirects to login.html.

After successful username/password authentication, the user is redirected to ott.html to request a one‑time token.

Submitting the form generates a token; OttSuccessHandler prints a link such as http://localhost:8080/login/ott?token=….

Opening the link shows the /ott/sent confirmation page and then redirects back to the originally requested /admin endpoint, completing MFA.

Console output example:

Jump link: http://localhost:8080/login/ott?token=46c7ed8b-367c-4b63-a5a6-425f0f793f1b

This example demonstrates how to integrate password‑plus‑one‑time‑token MFA into a Spring Boot 3 application, covering dependencies, controller definition, security configuration, token handling, custom UI, and the end‑to‑end verification process.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring SecurityMFAspring-bootone-time-tokenmultifactor-authentication
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.