diff --git a/docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc b/docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc index 145e1db0b3..df21b2cf3c 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc @@ -31,18 +31,20 @@ The One-Time Token Login works in two major steps. 1. User requests a token by submitting their user identifier, usually the username, and the token is delivered to them, often as a Magic Link, via e-mail, SMS, etc. 2. User submits the token to the one-time token login endpoint and, if valid, the user gets logged in. +In the following sections we will explore how to configure OTT Login for your needs. + +- <> +- <> +- <> +- <> +- <> + [[default-pages]] == Default Login Page and Default One-Time Token Submit Page The `oneTimeTokenLogin()` DSL can be used in conjunction with `formLogin()`, which will produce an additional One-Time Token Request Form in the xref:servlet/authentication/passwords/form.adoc[default generated login page]. It will also set up the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] to generate a default One-Time Token submit page. -In the following sections we will explore how to configure OTT Login for your needs. - -- <> -- <> -- <> - [[sending-token-to-user]] == Sending the Token to the User @@ -63,7 +65,7 @@ Java:: public class SecurityConfig { @Bean - public SecurityFilterChain filterChain(HttpSecurity http, MagicLinkGeneratedOneTimeTokenSuccessHandler magicLinkSender) { + public SecurityFilterChain filterChain(HttpSecurity http, MagicLinkGeneratedOneTimeTokenHandler magicLinkSender) { http // ... .formLogin(Customizer.withDefaults()) @@ -77,7 +79,7 @@ import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; @Component <1> -public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenHandler { +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { private final MailSender mailSender; @@ -177,7 +179,7 @@ class PageController { ---- ====== -<1> Make the `MagicLinkGeneratedOneTimeTokenSuccessHandler` a Spring bean +<1> Make the `MagicLinkGeneratedOneTimeTokenHandler` a Spring bean <2> Create a login processing URL with the `token` as a query param <3> Retrieve the user's email based on the username <4> Use the `JavaMailSender` API to send the email to the user with the magic link @@ -220,7 +222,7 @@ public class SecurityConfig { } @Component -public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenHandler { +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { // ... } ---- @@ -284,7 +286,7 @@ public class SecurityConfig { } @Component -public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenSuccessHandler { +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { // ... } ---- @@ -360,7 +362,7 @@ public class MyController { } @Component -public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenSuccessHandler { +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { // ... } ---- @@ -405,4 +407,143 @@ class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandle ---- ====== +[[customize-generate-consume-token]] +== Customize How to Generate and Consume One-Time Tokens +The interface that define the common operations for generating and consuming one-time tokens is the javadoc:org.springframework.security.authentication.ott.OneTimeTokenService[]. +Spring Security uses the javadoc:org.springframework.security.authentication.ott.InMemoryOneTimeTokenService[] as the default implementation of that interface, if none is provided. + +Some of the most common reasons to customize the `OneTimeTokenService` are, but not limited to: + +- Changing the one-time token expire time +- Storing more information from the generate token request +- Changing how the token value is created +- Additional validation when consuming a one-time token + +There are two options to customize the `OneTimeTokenService`. +One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL: + +.Passing the OneTimeTokenService as a Bean +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) { + http + // ... + .formLogin(Customizer.withDefaults()) + .oneTimeTokenLogin(Customizer.withDefaults()); + return http.build(); + } + + @Bean + public OneTimeTokenService oneTimeTokenService() { + return new MyCustomOneTimeTokenService(); + } + +} + +@Component +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { + // ... +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Configuration +@EnableWebSecurity +class SecurityConfig { + + @Bean + open fun filterChain(http: HttpSecurity): SecurityFilterChain { + http { + //... + formLogin { } + oneTimeTokenLogin { } + } + return http.build() + } + + @Bean + open fun oneTimeTokenService(): OneTimeTokenService { + return MyCustomOneTimeTokenService() + } +} + +@Component +class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandler { + // ... +} +---- +====== + +The second option is to pass the `OneTimeTokenService` instance to the DSL, which is useful if there are multiple `SecurityFilterChain` and a different `OneTimeTokenService` is needed for each of them. + +.Passing the OneTimeTokenService using the DSL +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) { + http + // ... + .formLogin(Customizer.withDefaults()) + .oneTimeTokenLogin((ott) -> ott + .oneTimeTokenService(new MyCustomOneTimeTokenService()) + ); + return http.build(); + } + +} + +@Component +public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler { + // ... +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Configuration +@EnableWebSecurity +class SecurityConfig { + + @Bean + open fun filterChain(http: HttpSecurity): SecurityFilterChain { + http { + //... + formLogin { } + oneTimeTokenLogin { + oneTimeTokenService = MyCustomOneTimeTokenService() + } + } + return http.build() + } + +} + +@Component +class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandler { + // ... +} +---- +======