1
0
mirror of synced 2026-09-11 11:49:43 +00:00

Merge branch '7.0.x' into 7.1.x

This commit is contained in:
Josh Cummings
2026-09-03 15:01:11 -06:00
59 changed files with 2363 additions and 943 deletions
@@ -53,138 +53,13 @@ Therefore, a custom javadoc:org.springframework.security.web.server.authenticati
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
In the following example, we are going to create a magic link and sent it to the user's email.
.One-Time Token Login Configuration
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@Component <1>
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
private final MailSender mailSender;
private final ServerOneTimeTokenGenerationSuccessHandler redirectHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
// constructor omitted
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
return Mono.just(exchange.getRequest())
.map((request) ->
UriComponentsBuilder.fromUri(request.getURI())
.replacePath(request.getPath().contextPath().value())
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue())
.toUriString() <2>
)
.flatMap((uri) -> this.mailSender.send(getUserEmail(oneTimeToken.getUsername()), <3>
"Use the following link to sign in into the application: " + magicLink)) <4>
.then(this.redirectHandler.handle(exchange, oneTimeToken)); <5>
}
private String getUserEmail() {
// ...
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
String ottSent() {
return "my-template";
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oneTimeTokenLogin { }
}
}
}
@Component (1)
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
private val redirectStrategy: ServerRedirectStrategy = DefaultServerRedirectStrategy()
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
val builder = UriComponentsBuilder.fromUri(exchange.request.uri)
.replacePath(null)
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue()) (2)
val magicLink = builder.toUriString()
builder.replacePath(null)
.replaceQuery(null)
.path("/ott/sent")
val redirectLink = builder.toUriString()
return this.mailSender.send(
getUserEmail(oneTimeToken.getUsername()), (3)
"Use the following link to sign in into the application: $magicLink") (4)
.then(this.redirectStrategy.sendRedirect(exchange, URI.create(redirectLink))) (5)
}
private String getUserEmail() {
// ...
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
fun ottSent(): String {
return "my-template"
}
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` 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 `MailSender` API to send the email to the user with the magic link
<5> Use the `ServerRedirectStrategy` to perform a redirect to your desired URL
<5> Use the `ServerOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL
The email content will look similar to:
@@ -196,65 +71,10 @@ The default submit page will detect that the URL has the `token` query param and
== Changing the One-Time Token Generate URL
By default, the javadoc:org.springframework.security.web.server.authentication.ott.GenerateOneTimeTokenWebFilter[] listens to `POST /ott/generate` requests.
That URL can be changed by using the `generateTokenUrl(String)` DSL method:
That URL can be changed by using the `tokenGeneratingUrl(String)` DSL method:
.Changing the Generate URL
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.generateTokenUrl("/ott/my-generate-url")
);
return http.build();
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
formLogin { }
oneTimeTokenLogin {
generateTokenUrl = "/ott/my-generate-url"
}
}
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[[changing-submit-page-url]]
== Changing the Default Submit Page URL
@@ -262,151 +82,17 @@ class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender):
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.server.ui.OneTimeTokenSubmitPageGeneratingWebFilter[] and listens to `GET /login/ott`.
The URL can also be changed, like so:
.Configuring the Default Submit Page URL
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.submitPageUrl("/ott/submit")
);
return http.build();
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
formLogin { }
oneTimeTokenLogin {
submitPageUrl = "/ott/submit"
}
}
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[[disabling-default-submit-page]]
== Disabling the Default Submit Page
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
.Disabling the Default Submit Page
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.pathMatchers("/my-ott-submit").permitAll()
.anyExchange().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.showDefaultSubmitPage(false)
);
return http.build();
}
}
@Controller
public class MyController {
@GetMapping("/my-ott-submit")
public String ottSubmitPage() {
return "my-ott-submit";
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(pathMatchers("/my-ott-submit"), permitAll)
authorize(anyExchange, authenticated)
}
.formLogin { }
oneTimeTokenLogin {
showDefaultSubmitPage = false
}
}
}
}
@Controller
class MyController {
@GetMapping("/my-ott-submit")
fun ottSubmitPage(): String {
return "my-ott-submit"
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
include-code::./MyController[tag=snippet,indent=0]
[[customize-generate-consume-token]]
== Customize How to Generate and Consume One-Time Tokens
@@ -424,157 +110,16 @@ Some of the most common reasons to customize the `ReactiveOneTimeTokenService` a
There are two options to customize the `ReactiveOneTimeTokenService`.
One option is to provide it as a bean, so it can be automatically be picked-up by the `oneTimeTokenLogin()` DSL:
.Passing the ReactiveOneTimeTokenService as a Bean
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public ReactiveOneTimeTokenService oneTimeTokenService() {
return new MyCustomReactiveOneTimeTokenService();
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
//..
.formLogin { }
oneTimeTokenLogin { }
}
}
@Bean
open fun oneTimeTokenService():ReactiveOneTimeTokenService {
return MyCustomReactiveOneTimeTokenService();
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./OneTimeTokenServiceBeanSecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
The second option is to pass the `ReactiveOneTimeTokenService` instance to the DSL, which is useful if there are multiple ``SecurityWebFilterChain``s and a different ``ReactiveOneTimeTokenService``s is needed for each of them.
.Passing the ReactiveOneTimeTokenService using the DSL
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.oneTimeTokenService(new MyCustomReactiveOneTimeTokenService())
);
return http.build();
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebFluxSecurity
class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
//..
.formLogin { }
oneTimeTokenLogin {
oneTimeTokenService = MyCustomReactiveOneTimeTokenService()
}
}
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender): ServerOneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./OneTimeTokenServiceDSLSecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[[customize-generate-token-request]]
== Customize GenerateOneTimeTokenRequest Instance
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
You can customize elements of GenerateOneTimeTokenRequest by publishing an ServerGenerateOneTimeTokenRequestResolver as a @Bean, like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
ServerGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();
resolver.setExpiresIn(Duration.ofSeconds(600));
return resolver;
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun generateOneTimeTokenRequestResolver() : ServerGenerateOneTimeTokenRequestResolver {
return DefaultServerGenerateOneTimeTokenRequestResolver().apply {
this.setExpiresIn(Duration.ofMinutes(10))
}
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
@@ -53,133 +53,12 @@ Therefore, a custom javadoc:org.springframework.security.web.authentication.ott.
One of the most common delivery strategies is a Magic Link, via e-mail, SMS, etc.
In the following example, we are going to create a magic link and sent it to the user's email.
.One-Time Token Login Configuration
[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();
}
}
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@Component <1>
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
private final MailSender mailSender;
private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
// constructor omitted
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue()); <2>
String magicLink = builder.toUriString();
String email = getUserEmail(oneTimeToken.getUsername()); <3>
this.mailSender.send(email, "Your Spring Security One Time Token", "Use the following link to sign in into the application: " + magicLink); <4>
this.redirectHandler.handle(request, response, oneTimeToken); <5>
}
private String getUserEmail() {
// ...
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
String ottSent() {
return "my-template";
}
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http{
formLogin {}
oneTimeTokenLogin { }
}
return http.build()
}
}
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@Component (1)
class MagicLinkOneTimeTokenGenerationSuccessHandler(
private val mailSender: MailSender,
private val redirectHandler: OneTimeTokenGenerationSuccessHandler = RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent")
) : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
val builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
.replacePath(request.contextPath)
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue()) (2)
val magicLink = builder.toUriString()
val email = getUserEmail(oneTimeToken.getUsername()) (3)
this.mailSender.send(email, "Your Spring Security One Time Token", "Use the following link to sign in into the application: $magicLink")(4)
this.redirectHandler.handle(request, response, oneTimeToken) (5)
}
private fun getUserEmail(): String {
// ...
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
fun ottSent(): String {
return "my-template"
}
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
<1> Make the `MagicLinkOneTimeTokenGenerationSuccessHandler` 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
<4> Use the `MailSender` API to send the email to the user with the magic link
<5> Use the `RedirectOneTimeTokenGenerationSuccessHandler` to perform a redirect to your desired URL
The email content will look similar to:
@@ -192,70 +71,15 @@ The default submit page will detect that the URL has the `token` query param and
== Changing the One-Time Token Generate URL
By default, the javadoc:org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter[] listens to `POST /ott/generate` requests.
That URL can be changed by using the `generateTokenUrl(String)` DSL method:
That URL can be changed by using the `tokenGeneratingUrl(String)` DSL method:
.Changing the Generate URL
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenGeneratingUrl("/ott/my-generate-url")
);
return http.build();
}
}
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin {
tokenGeneratingUrl = "/ott/my-generate-url"
}
}
return http.build()
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[NOTE]
====
The URI passed to `generateTokenUrl` is matched literally.
If you want it to live under your application servlet's base path, include that prefix explicitly — for example, `generateTokenUrl("/api/ott/generate")`.
The URI passed to `tokenGeneratingUrl` is matched literally.
If you want it to live under your application servlet's base path, include that prefix explicitly — for example, `tokenGeneratingUrl("/api/ott/generate")`.
The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on this page.
====
@@ -265,151 +89,17 @@ The same applies to `loginProcessingUrl` and `tokenGeneratingUrl` elsewhere on t
The default One-Time Token submit page is generated by the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] and listens to `GET /login/ott`.
The URL can also be changed, like so:
.Configuring the Default Submit Page URL
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.defaultSubmitPageUrl("/ott/submit")
);
return http.build();
}
}
@Component
public class MagicLinkGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin {
defaultSubmitPageUrl = "/ott/submit"
}
}
return http.build()
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[[disabling-default-submit-page]]
== Disabling the Default Submit Page
If you want to use your own One-Time Token submit page, you can disable the default page and then provide your own endpoint.
.Disabling the Default Submit Page
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my-ott-submit").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.showDefaultSubmitPage(false)
);
return http.build();
}
}
@Controller
public class MyController {
@GetMapping("/my-ott-submit")
public String ottSubmitPage() {
return "my-ott-submit";
}
}
@Component
public class OneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
// ...
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/my-ott-submit", authenticated)
authorize(anyRequest, authenticated)
}
formLogin { }
oneTimeTokenLogin {
showDefaultSubmitPage = false
}
}
return http.build()
}
}
@Controller
class MyController {
@GetMapping("/my-ott-submit")
fun ottSubmitPage(): String {
return "my-ott-submit"
}
}
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
include-code::./MyController[tag=snippet,indent=0]
[[customize-generate-consume-token]]
== Customize How to Generate and Consume One-Time Tokens
@@ -428,161 +118,18 @@ Some of the most common reasons to customize the `OneTimeTokenService` are, but
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 MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
// ...
}
----
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 MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./OneTimeTokenServiceBeanSecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
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 MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
// ...
}
----
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 MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
// ...
}
----
======
include-code::./OneTimeTokenServiceDSLSecurityConfig[tag=config,indent=0]
include-code::./MagicLinkOneTimeTokenGenerationSuccessHandler[tag=snippet,indent=0]
[[customize-generate-token-request]]
== Customize GenerateOneTimeTokenRequest Instance
There are a number of reasons that you may want to adjust an GenerateOneTimeTokenRequest. For example, you may want expiresIn to be set to 10 mins, which Spring Security sets to 5 mins by default.
You can customize elements of GenerateOneTimeTokenRequest by publishing an GenerateOneTimeTokenRequestResolver as a @Bean, like so:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();
return (request) -> {
GenerateOneTimeTokenRequest generate = delegate.resolve(request);
return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofSeconds(600));
};
}
----
You can customize elements of GenerateOneTimeTokenRequest by publishing an GenerateOneTimeTokenRequestResolver as a `@Bean`, like so:
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun generateRequestResolver() : GenerateOneTimeTokenRequestResolver {
return DefaultGenerateOneTimeTokenRequestResolver().apply {
this.setExpiresIn(Duration.ofMinutes(10))
}
}
----
======
include-code::./SecurityConfig[tag=config,indent=0]
+1
View File
@@ -49,6 +49,7 @@ dependencies {
testImplementation 'org.springframework:spring-websocket'
testImplementation 'org.springframework:spring-webmvc'
testImplementation 'org.springframework:spring-context-support'
testImplementation 'jakarta.servlet:jakarta.servlet-api'
testImplementation 'io.mockk:mockk'
testImplementation "org.junit.jupiter:junit-jupiter-api"
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.changinggenerateurl;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
/**/ return Mono.empty();
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.changinggenerateurl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenGeneratingUrl("/ott/my-generate-url")
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.changingsubmitpageurl;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
/**/ return Mono.empty();
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.changingsubmitpageurl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.defaultSubmitPageUrl("/ott/submit")
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
/**/ return Mono.empty();
}
}
// end::snippet[]
@@ -0,0 +1,38 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;
import reactor.core.publisher.Mono;
class MyCustomReactiveOneTimeTokenService implements ReactiveOneTimeTokenService {
@Override
public Mono<OneTimeToken> generate(GenerateOneTimeTokenRequest request) {
return null;
}
@Override
public Mono<OneTimeToken> consume(OneTimeTokenAuthenticationToken authenticationToken) {
return null;
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken.MyCustomReactiveOneTimeTokenService;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class OneTimeTokenServiceBeanSecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public ReactiveOneTimeTokenService oneTimeTokenService() {
return new MyCustomReactiveOneTimeTokenService();
}
}
// end::config[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.customizegenerateconsumetoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebFluxSecurity
public class OneTimeTokenServiceDSLSecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenService(new MyCustomReactiveOneTimeTokenService())
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.customizegeneratetokenrequest;
import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver;
public class SecurityConfig {
// tag::config[]
@Bean
ServerGenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
DefaultServerGenerateOneTimeTokenRequestResolver resolver = new DefaultServerGenerateOneTimeTokenRequestResolver();
resolver.setExpiresIn(Duration.ofMinutes(10));
return resolver;
}
// end::config[]
}
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
/**/ return Mono.empty();
}
}
// end::snippet[]
@@ -0,0 +1,32 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
// tag::snippet[]
@Controller
public class MyController {
@GetMapping("/my-ott-submit")
public String ottSubmitPage() {
return "my-ott-submit";
}
}
// end::snippet[]
@@ -0,0 +1,46 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.disablingdefaultsubmitpage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.pathMatchers("/my-ott-submit").permitAll()
.anyExchange().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.showDefaultSubmitPage(false)
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,84 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.sendingtokentouser;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
// tag::snippet[]
@Component // <1>
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements ServerOneTimeTokenGenerationSuccessHandler {
private final MailSender mailSender;
private final ServerOneTimeTokenGenerationSuccessHandler redirectHandler = new ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
public MagicLinkOneTimeTokenGenerationSuccessHandler(MailSender mailSender) {
this.mailSender = mailSender;
}
@Override
public Mono<Void> handle(ServerWebExchange exchange, OneTimeToken oneTimeToken) {
return Mono.just(exchange.getRequest())
.map((request) ->
UriComponentsBuilder.fromUri(request.getURI())
.replacePath(request.getPath().contextPath().value())
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue())
.toUriString() // <2>
)
.flatMap((uri) -> {
String email = getUserEmail(oneTimeToken.getUsername()); // <3>
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("Your Spring Security One Time Token");
message.setText("Use the following link to sign in into the application: " + uri);
this.mailSender.send(message); // <4>
return Mono.empty();
})
.then(this.redirectHandler.handle(exchange, oneTimeToken)); // <5>
}
private String getUserEmail(String username) {
/**/ return username;
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
String ottSent() {
return "my-template";
}
}
// end::snippet[]
@@ -0,0 +1,41 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.reactive.authentication.sendingtokentouser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
// tag::config[]
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.changinggenerateurl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.changinggenerateurl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenGeneratingUrl("/ott/my-generate-url")
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.changingsubmitpageurl;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.changingsubmitpageurl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.defaultSubmitPageUrl("/ott/submit")
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,37 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken;
import org.springframework.security.authentication.ott.OneTimeTokenService;
class MyCustomOneTimeTokenService implements OneTimeTokenService {
@Override
public OneTimeToken generate(GenerateOneTimeTokenRequest request) {
return null;
}
@Override
public @Nullable OneTimeToken consume(OneTimeTokenAuthenticationToken authenticationToken) {
return null;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ott.OneTimeTokenService;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class OneTimeTokenServiceBeanSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public OneTimeTokenService oneTimeTokenService() {
return new MyCustomOneTimeTokenService();
}
}
// end::config[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.customizegenerateconsumetoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class OneTimeTokenServiceDSLSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenService(new MyCustomOneTimeTokenService())
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,39 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.customizegeneratetokenrequest;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;
import java.time.Duration;
public class SecurityConfig {
// tag::config[]
@Bean
GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();
return (request) -> {
GenerateOneTimeTokenRequest generate = delegate.resolve(request);
return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofMinutes(10));
};
}
// end::config[]
}
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.stereotype.Component;
// tag::snippet[]
@Component
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,32 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
// tag::snippet[]
@Controller
public class MyController {
@GetMapping("/my-ott-submit")
public String ottSubmitPage() {
return "my-ott-submit";
}
}
// end::snippet[]
@@ -0,0 +1,46 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.disablingdefaultsubmitpage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/my-ott-submit").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.showDefaultSubmitPage(false)
);
return http.build();
}
}
// end::config[]
@@ -0,0 +1,80 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.sendingtokentouser;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.IOException;
// tag::snippet[]
@Component // <1>
public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTokenGenerationSuccessHandler {
private final MailSender mailSender;
private final OneTimeTokenGenerationSuccessHandler redirectHandler = new RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent");
public MagicLinkOneTimeTokenGenerationSuccessHandler(MailSender mailSender) {
this.mailSender = mailSender;
}
@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()); // <2>
String magicLink = builder.toUriString();
String email = getUserEmail(oneTimeToken.getUsername()); // <3>
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("Your Spring Security One Time Token");
message.setText("Use the following link to sign in into the application: " + magicLink);
this.mailSender.send(message); // <4>
this.redirectHandler.handle(request, response, oneTimeToken); // <5>
}
private String getUserEmail(String username) {
/**/ return username;
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
String ottSent() {
return "my-template";
}
}
// end::snippet[]
@@ -0,0 +1,41 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.docs.servlet.authentication.sendingtokentouser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
// tag::config[]
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin(Customizer.withDefaults());
return http.build();
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.changinggenerateurl
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
/**/ return Mono.empty()
}
}
// end::snippet[]
@@ -0,0 +1,41 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.changinggenerateurl
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
formLogin { }
oneTimeTokenLogin {
tokenGeneratingUrl = "/ott/my-generate-url"
}
}
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.changingsubmitpageurl
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
/**/ return Mono.empty()
}
}
// end::snippet[]
@@ -0,0 +1,41 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.changingsubmitpageurl
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
formLogin { }
oneTimeTokenLogin {
defaultSubmitPageUrl = "/ott/submit"
}
}
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
/**/ return Mono.empty()
}
}
// end::snippet[]
@@ -0,0 +1,36 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService
import reactor.core.publisher.Mono
class MyCustomReactiveOneTimeTokenService: ReactiveOneTimeTokenService {
override fun generate(request: GenerateOneTimeTokenRequest): Mono<OneTimeToken> {
TODO("Not yet implemented")
}
override fun consume(authenticationToken: OneTimeTokenAuthenticationToken): Mono<OneTimeToken> {
TODO("Not yet implemented")
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.ott.reactive.ReactiveOneTimeTokenService
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class OneTimeTokenServiceBeanSecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
//..
formLogin { }
oneTimeTokenLogin { }
}
}
@Bean
open fun oneTimeTokenService(): ReactiveOneTimeTokenService {
return MyCustomReactiveOneTimeTokenService()
}
}
// end::config[]
@@ -0,0 +1,41 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.customizegenerateconsumetoken
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class OneTimeTokenServiceDSLSecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
//..
formLogin { }
oneTimeTokenLogin {
tokenService = MyCustomReactiveOneTimeTokenService()
}
}
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.customizegeneratetokenrequest
import org.springframework.context.annotation.Bean
import org.springframework.security.web.server.authentication.ott.DefaultServerGenerateOneTimeTokenRequestResolver
import org.springframework.security.web.server.authentication.ott.ServerGenerateOneTimeTokenRequestResolver
import java.time.Duration
open class SecurityConfig {
// tag::config[]
@Bean
fun generateOneTimeTokenRequestResolver() : ServerGenerateOneTimeTokenRequestResolver {
return DefaultServerGenerateOneTimeTokenRequestResolver().apply {
this.setExpiresIn(Duration.ofMinutes(10))
}
}
// end::config[]
}
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : ServerOneTimeTokenGenerationSuccessHandler {
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
/**/ return Mono.empty()
}
}
// end::snippet[]
@@ -0,0 +1,32 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
// tag::snippet[]
@Controller
class MyController {
@GetMapping("/my-ott-submit")
fun ottSubmitPage(): String {
return "my-ott-submit"
}
}
// end::snippet[]
@@ -0,0 +1,45 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.disablingdefaultsubmitpage
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(pathMatchers("/my-ott-submit"), permitAll)
authorize(anyExchange, authenticated)
}
formLogin { }
oneTimeTokenLogin {
showDefaultSubmitPage = false
}
}
}
}
// end::config[]
@@ -0,0 +1,78 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.sendingtokentouser
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.server.authentication.ott.ServerOneTimeTokenGenerationSuccessHandler
import org.springframework.security.web.server.authentication.ott.ServerRedirectOneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.util.UriComponentsBuilder
import reactor.core.publisher.Mono
import java.util.function.Function
// tag::snippet[]
@Component // <1>
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender) : ServerOneTimeTokenGenerationSuccessHandler {
private val redirectHandler: ServerOneTimeTokenGenerationSuccessHandler = ServerRedirectOneTimeTokenGenerationSuccessHandler("/ott/sent")
override fun handle(exchange: ServerWebExchange, oneTimeToken: OneTimeToken): Mono<Void> {
return Mono.just(exchange.request)
.map(Function { request ->
UriComponentsBuilder.fromUri(request.uri)
.replacePath(request.path.contextPath().value())
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue())
.toUriString() // <2>
})
.flatMap(Function { uri ->
val email = getUserEmail(oneTimeToken.getUsername()) // <3>
val message = SimpleMailMessage()
message.setTo(email)
message.subject = "Your Spring Security One Time Token"
message.text = "Use the following link to sign in into the application: $uri"
this.mailSender.send(message) // <4>
Mono.empty()
})
.then(this.redirectHandler.handle(exchange, oneTimeToken)) // <5>
}
private fun getUserEmail(username: String): String {
/**/ return username
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
fun ottSent(): String {
return "my-template"
}
}
// end::snippet[]
@@ -0,0 +1,40 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.reactive.authentication.sendingtokentouser
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.config.web.server.invoke
import org.springframework.security.web.server.SecurityWebFilterChain
// tag::config[]
@Configuration
@EnableWebFluxSecurity
open class SecurityConfig {
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
authorizeExchange {
authorize(anyExchange, authenticated)
}
oneTimeTokenLogin { }
}
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.changinggenerateurl
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.changinggenerateurl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin {
tokenGeneratingUrl = "/ott/my-generate-url"
}
}
return http.build()
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.changingsubmitpageurl
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,43 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.changingsubmitpageurl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin {
defaultSubmitPageUrl = "/ott/submit"
}
}
return http.build()
}
}
// end::config[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.authentication.ott.OneTimeTokenAuthenticationToken
import org.springframework.security.authentication.ott.OneTimeTokenService
class MyCustomOneTimeTokenService: OneTimeTokenService {
override fun generate(request: GenerateOneTimeTokenRequest): OneTimeToken {
TODO("Not yet implemented")
}
override fun consume(authenticationToken: OneTimeTokenAuthenticationToken): OneTimeToken? {
TODO("Not yet implemented")
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.ott.OneTimeTokenService
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class OneTimeTokenServiceBeanSecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin { }
}
return http.build()
}
@Bean
open fun oneTimeTokenService(): OneTimeTokenService {
return MyCustomOneTimeTokenService()
}
}
// end::config[]
@@ -0,0 +1,44 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.customizegenerateconsumetoken
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class OneTimeTokenServiceDSLSecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
//...
formLogin { }
oneTimeTokenLogin {
tokenService = MyCustomOneTimeTokenService()
}
}
return http.build()
}
}
// end::config[]
@@ -0,0 +1,35 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.customizegeneratetokenrequest
import org.springframework.context.annotation.Bean
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver
import java.time.Duration
open class SecurityConfig {
// tag::config[]
@Bean
open fun generateRequestResolver() : GenerateOneTimeTokenRequestResolver {
return DefaultGenerateOneTimeTokenRequestResolver().apply {
this.setExpiresIn(Duration.ofMinutes(10))
}
}
// end::config[]
}
@@ -0,0 +1,34 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.disablingdefaultsubmitpage
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.stereotype.Component
// tag::snippet[]
@Component
class MagicLinkOneTimeTokenGenerationSuccessHandler : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
// ...
}
}
// end::snippet[]
@@ -0,0 +1,32 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.disablingdefaultsubmitpage
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
// tag::snippet[]
@Controller
class MyController {
@GetMapping("/my-ott-submit")
fun ottSubmitPage(): String {
return "my-ott-submit"
}
}
// end::snippet[]
@@ -0,0 +1,47 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.disablingdefaultsubmitpage
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/my-ott-submit", authenticated)
authorize(anyRequest, authenticated)
}
formLogin { }
oneTimeTokenLogin {
showDefaultSubmitPage = false
}
}
return http.build()
}
}
// end::config[]
@@ -0,0 +1,69 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.sendingtokentouser
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
import org.springframework.security.authentication.ott.OneTimeToken
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler
import org.springframework.security.web.util.UrlUtils
import org.springframework.stereotype.Component
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.util.UriComponentsBuilder
// tag::snippet[]
@Component // <1>
class MagicLinkOneTimeTokenGenerationSuccessHandler(val mailSender: MailSender) : OneTimeTokenGenerationSuccessHandler {
private val redirectHandler: OneTimeTokenGenerationSuccessHandler = RedirectOneTimeTokenGenerationSuccessHandler("/ott/sent")
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
val builder = UriComponentsBuilder.fromUriString(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.contextPath)
.replaceQuery(null)
.fragment(null)
.path("/login/ott")
.queryParam("token", oneTimeToken.getTokenValue()) // <2>
val magicLink = builder.toUriString()
val email = getUserEmail(oneTimeToken.getUsername()) // <3>
val message = SimpleMailMessage()
message.setTo(email)
message.subject = "Your Spring Security One Time Token"
message.text = "Use the following link to sign in into the application: $magicLink"
this.mailSender.send(message) // <4>
this.redirectHandler.handle(request, response, oneTimeToken) // <5>
}
private fun getUserEmail(username: String): String {
/**/ return username
}
}
@Controller
class PageController {
@GetMapping("/ott/sent")
fun ottSent(): String {
return "my-template"
}
}
// end::snippet[]
@@ -0,0 +1,40 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.kt.docs.servlet.authentication.sendingtokentouser
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain
// tag::config[]
@Configuration
@EnableWebSecurity
open class SecurityConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
formLogin { }
oneTimeTokenLogin { }
}
return http.build()
}
}
// end::config[]