Logging into an application should be simple for users but still safe and secure. Traditional logins with usernames and passwords work, but they can be inconvenient and sometimes risky if passwords are stolen. A one-time token login offers a solution—it’s both easy for users and secure.

In this blog post, I’ll show you how to build a login system using one-time tokens in a Spring Boot application. We’ll use Spring Security and integrate it with Vaadin.

Spring Security One-Time Token Login

In short, One-Time Tokens (OTT) offer a simple way to authenticate users without requiring extra account setup. This sets them apart from One-Time Passwords (OTP), which usually involve a more complicated setup process and depend on external tools for generating tokens.

Since Spring Security 6.4, support for One-Time Tokens is available. This login method works in two simple steps:

  1. The user requests a token by providing their identifier, usually a username. The token is then sent to them via email, SMS, or another method, often as a Magic Link.
  2. The user submits the token to the one-time token login endpoint. If the token is valid, they are logged in successfully.

Security Configuration with Vaadin

In this section we weill configure Spring Security for Vaadin. VaadinWebSecurity offers Spring Security integation with Vaadin and is used as the base class.

Configure Login

The one-time token login is configured on line 11 and 12. The standard Login form is used that will then also contain a field to submit the one-time token. In a later post I will show how to integrate the form with Vaadin.

@EnableWebSecurity
@Configuration
public class SecurityConfiguration extends VaadinWebSecurity {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(c -> c
                .requestMatchers(new AntPathRequestMatcher("/images/*.png"))
                .permitAll());

        http.formLogin(Customizer.withDefaults())
                .oneTimeTokenLogin(Customizer.withDefaults());

        super.configure(http);
    }
}

Send Magic Link

To send the token to the user a OneTimeTokenGenerationSuccessHandler must be implemented.
This is a dummy implementation and just prints out the magic link to the console. In real-world we would send the token via email or other messaging to the user.

Important is the path that is passed when creating the RedirectOneTimeTokenGenerationSuccessHandler. This will the path the user will be redirected to after the successful login.

@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {

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

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))
                .replacePath(request.getContextPath())
                .replaceQuery(null)
                .fragment(null)
                .path("/login/ott")
                .queryParam("token", oneTimeToken.getTokenValue());
        String magicLink = builder.toUriString();

        System.out.println(magicLink);

        this.redirectHandler.handle(request, response, oneTimeToken);
    }
}

Secure the Vaadin Route

We can use role-based security as usual:

@RolesAllowed({Roles.USER, Roles.ADMIN})
@Route("")
public class MainView extends VerticalLayout {

    public MainView() {
        add(new H1("Vaadin OTT Demo"));

        add(new H2("You are logged in as " + SecurityContextHolder.getContext().getAuthentication().getName()));
    }
}

Action!

1. The user tries to access https://localhost:8080. The login form will be displayed:

2. The user enters the username and clicks on Send Token:

    3. The magic link is generated and the user clicks on the link:
    http://localhost:8080/login/ott?token=6b1bcd19-f30f-41d1-b0f9-e8fc770ddebd

    4. The user is redirected to a page to enter the token that is already prefilled:

    5. The user is redirected to the path defined in the RedirectOneTimeTokenGenerationSuccessHandler

    Conclusion and Links

    Integrating a one-time token login in Vaadin is simple thanks to the superb Spring Security support.

    You can find a detailed description in the Spring Security documentation: https://docs.spring.io/spring-security/reference/servlet/authentication/onetimetoken.html

    The source code of this example is available on Github: https://github.com/simasch/vaadin-one-time-token-login