Skip to content

Commit 6417eb7

Browse files
Document how to customize OneTimeTokenService
Closes gh-15743
1 parent 6428bf2 commit 6417eb7

File tree

1 file changed

+153
-12
lines changed

1 file changed

+153
-12
lines changed

docs/modules/ROOT/pages/servlet/authentication/onetimetoken.adoc

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,19 @@ The One-Time Token Login works in two major steps.
3131
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.
3232
2. User submits the token to the one-time token login endpoint and, if valid, the user gets logged in.
3333

34-
[[default-pages]]
35-
== Default Login Page and Default One-Time Token Submit Page
36-
37-
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].
38-
It will also set up the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] to generate a default One-Time Token submit page.
39-
4034
In the following sections we will explore how to configure OTT Login for your needs.
4135

36+
- <<default-pages,Understanding the integration with the default generated login page>>
4237
- <<sending-token-to-user,Sending the token to the user>>
4338
- <<changing-submit-page-url,Configuring the One-Time Token submit page>>
4439
- <<changing-generate-url,Changing the One-Time Token generate URL>>
40+
- <<customize-generate-consume-token,Customize how to generate and consume tokens>>
41+
42+
[[default-pages]]
43+
== Default Login Page and Default One-Time Token Submit Page
44+
45+
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].
46+
It will also set up the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] to generate a default One-Time Token submit page.
4547

4648
[[sending-token-to-user]]
4749
== Sending the Token to the User
@@ -63,7 +65,7 @@ Java::
6365
public class SecurityConfig {
6466
6567
@Bean
66-
public SecurityFilterChain filterChain(HttpSecurity http, MagicLinkGeneratedOneTimeTokenSuccessHandler magicLinkSender) {
68+
public SecurityFilterChain filterChain(HttpSecurity http, MagicLinkGeneratedOneTimeTokenHandler magicLinkSender) {
6769
http
6870
// ...
6971
.formLogin(Customizer.withDefaults())
@@ -77,7 +79,7 @@ import org.springframework.mail.SimpleMailMessage;
7779
import org.springframework.mail.javamail.JavaMailSender;
7880
7981
@Component <1>
80-
public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenHandler {
82+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
8183
8284
private final MailSender mailSender;
8385
@@ -177,7 +179,7 @@ class PageController {
177179
----
178180
======
179181

180-
<1> Make the `MagicLinkGeneratedOneTimeTokenSuccessHandler` a Spring bean
182+
<1> Make the `MagicLinkGeneratedOneTimeTokenHandler` a Spring bean
181183
<2> Create a login processing URL with the `token` as a query param
182184
<3> Retrieve the user's email based on the username
183185
<4> Use the `JavaMailSender` API to send the email to the user with the magic link
@@ -220,7 +222,7 @@ public class SecurityConfig {
220222
}
221223
222224
@Component
223-
public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenHandler {
225+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
224226
// ...
225227
}
226228
----
@@ -284,7 +286,7 @@ public class SecurityConfig {
284286
}
285287
286288
@Component
287-
public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenSuccessHandler {
289+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
288290
// ...
289291
}
290292
----
@@ -360,7 +362,7 @@ public class MyController {
360362
}
361363
362364
@Component
363-
public class MagicLinkGeneratedOneTimeTokenSuccessHandler implements GeneratedOneTimeTokenSuccessHandler {
365+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
364366
// ...
365367
}
366368
----
@@ -405,4 +407,143 @@ class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandle
405407
----
406408
======
407409

410+
[[customize-generate-consume-token]]
411+
== Customize How to Generate and Consume One-Time Tokens
412+
413+
The interface that define the common operations for generating and consuming one-time tokens is the javadoc:org.springframework.security.authentication.ott.OneTimeTokenService[].
414+
Spring Security uses the javadoc:org.springframework.security.authentication.ott.InMemoryOneTimeTokenService[] as the default implementation of that interface, if none is provided.
415+
416+
Some of the most common reasons to customize the `OneTimeTokenService` are, but not limited to:
417+
418+
- Changing the one-time token expire time
419+
- Storing more information from the generate token request
420+
- Changing how the token value is created
421+
- Additional validation when consuming a one-time token
408422

423+
There are two options to customize the `OneTimeTokenService`.
424+
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
425+
426+
.Passing the OneTimeTokenService as a Bean
427+
[tabs]
428+
======
429+
Java::
430+
+
431+
[source,java,role="primary"]
432+
----
433+
@Configuration
434+
@EnableWebSecurity
435+
public class SecurityConfig {
436+
437+
@Bean
438+
public SecurityFilterChain filterChain(HttpSecurity http) {
439+
http
440+
// ...
441+
.formLogin(Customizer.withDefaults())
442+
.oneTimeTokenLogin(Customizer.withDefaults());
443+
return http.build();
444+
}
445+
446+
@Bean
447+
public OneTimeTokenService oneTimeTokenService() {
448+
return new MyCustomOneTimeTokenService();
449+
}
450+
451+
}
452+
453+
@Component
454+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
455+
// ...
456+
}
457+
----
458+
459+
Kotlin::
460+
+
461+
[source,kotlin,role="secondary"]
462+
----
463+
@Configuration
464+
@EnableWebSecurity
465+
class SecurityConfig {
466+
467+
@Bean
468+
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
469+
http {
470+
//...
471+
formLogin { }
472+
oneTimeTokenLogin { }
473+
}
474+
return http.build()
475+
}
476+
477+
@Bean
478+
open fun oneTimeTokenService(): OneTimeTokenService {
479+
return MyCustomOneTimeTokenService()
480+
}
481+
}
482+
483+
@Component
484+
class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandler {
485+
// ...
486+
}
487+
----
488+
======
489+
490+
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.
491+
492+
.Passing the OneTimeTokenService using the DSL
493+
[tabs]
494+
======
495+
Java::
496+
+
497+
[source,java,role="primary"]
498+
----
499+
@Configuration
500+
@EnableWebSecurity
501+
public class SecurityConfig {
502+
503+
@Bean
504+
public SecurityFilterChain filterChain(HttpSecurity http) {
505+
http
506+
// ...
507+
.formLogin(Customizer.withDefaults())
508+
.oneTimeTokenLogin((ott) -> ott
509+
.oneTimeTokenService(new MyCustomOneTimeTokenService())
510+
);
511+
return http.build();
512+
}
513+
514+
}
515+
516+
@Component
517+
public class MagicLinkGeneratedOneTimeTokenHandler implements GeneratedOneTimeTokenSuccessHandler {
518+
// ...
519+
}
520+
----
521+
522+
Kotlin::
523+
+
524+
[source,kotlin,role="secondary"]
525+
----
526+
@Configuration
527+
@EnableWebSecurity
528+
class SecurityConfig {
529+
530+
@Bean
531+
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
532+
http {
533+
//...
534+
formLogin { }
535+
oneTimeTokenLogin {
536+
oneTimeTokenService = MyCustomOneTimeTokenService()
537+
}
538+
}
539+
return http.build()
540+
}
541+
542+
}
543+
544+
@Component
545+
class MagicLinkGeneratedOneTimeTokenSuccessHandler : GeneratedOneTimeTokenHandler {
546+
// ...
547+
}
548+
----
549+
======

0 commit comments

Comments
 (0)