Add Support GenerateOneTimeTokenRequestResolver

Closes gh-16291

Signed-off-by: Max Batischev <mblancer@mail.ru>
This commit is contained in:
Max Batischev
2025-01-22 15:20:06 +03:00
committed by Rob Winch
parent 68c8a5ad99
commit 474b5e151a
13 changed files with 398 additions and 13 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@@ -17,6 +17,9 @@
package org.springframework.security.config.annotation.web.configurers.ott;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -29,6 +32,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.ott.GenerateOneTimeTokenRequest;
import org.springframework.security.authentication.ott.OneTimeToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -40,6 +44,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.ott.DefaultGenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenRequestResolver;
import org.springframework.security.web.authentication.ott.OneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.authentication.ott.RedirectOneTimeTokenGenerationSuccessHandler;
import org.springframework.security.web.csrf.CsrfToken;
@@ -194,6 +200,55 @@ public class OneTimeTokenLoginConfigurerTests {
""");
}
@Test
void oneTimeTokenWhenCustomTokenExpirationTimeSetThenAuthenticate() throws Exception {
this.spring.register(OneTimeTokenConfigWithCustomTokenExpirationTime.class).autowire();
this.mvc.perform(post("/ott/generate").param("username", "user").with(csrf()))
.andExpectAll(status().isFound(), redirectedUrl("/login/ott"));
OneTimeToken token = TestOneTimeTokenGenerationSuccessHandler.lastToken;
this.mvc.perform(post("/login/ott").param("token", token.getTokenValue()).with(csrf()))
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
assertThat(getCurrentMinutes(token.getExpiresAt())).isEqualTo(10);
}
private int getCurrentMinutes(Instant expiresAt) {
int expiresMinutes = expiresAt.atZone(ZoneOffset.UTC).getMinute();
int currentMinutes = Instant.now().atZone(ZoneOffset.UTC).getMinute();
return expiresMinutes - currentMinutes;
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
static class OneTimeTokenConfigWithCustomTokenExpirationTime {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.oneTimeTokenLogin((ott) -> ott
.tokenGenerationSuccessHandler(new TestOneTimeTokenGenerationSuccessHandler())
);
// @formatter:on
return http.build();
}
@Bean
GenerateOneTimeTokenRequestResolver generateOneTimeTokenRequestResolver() {
DefaultGenerateOneTimeTokenRequestResolver delegate = new DefaultGenerateOneTimeTokenRequestResolver();
return (request) -> {
GenerateOneTimeTokenRequest generate = delegate.resolve(request);
return new GenerateOneTimeTokenRequest(generate.getUsername(), Duration.ofSeconds(600));
};
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)