One Time Token login registers the default login page

closes gh-16414

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
This commit is contained in:
Daniel Garnier-Moiroux
2025-01-22 11:33:10 +01:00
committed by Rob Winch
parent 5ee6b83953
commit 238f47ce5e
10 changed files with 242 additions and 127 deletions

View File

@@ -146,8 +146,8 @@ public class OneTimeTokenLoginConfigurerTests {
}
@Test
void oneTimeTokenWhenFormLoginConfiguredThenRendersRequestTokenForm() throws Exception {
this.spring.register(OneTimeTokenFormLoginConfig.class).autowire();
void oneTimeTokenWhenConfiguredThenRendersRequestTokenForm() throws Exception {
this.spring.register(OneTimeTokenDefaultConfig.class).autowire();
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "BaseSpringSpec_CSRFTOKEN");
String csrfAttributeName = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
//@formatter:off
@@ -168,21 +168,7 @@ public class OneTimeTokenLoginConfigurerTests {
</head>
<body>
<div class="content">
<form class="login-form" method="post" action="/login">
<h2>Please sign in</h2>
<p>
<label for="username" class="screenreader">Username</label>
<input type="text" id="username" name="username" placeholder="Username" required autofocus>
</p>
<p>
<label for="password" class="screenreader">Password</label>
<input type="password" id="password" name="password" placeholder="Password" required>
</p>
<input name="_csrf" type="hidden" value="%s" />
<button type="submit" class="primary">Sign in</button>
</form>
<form id="ott-form" class="login-form" method="post" action="/ott/generate">
<h2>Request a One-Time Token</h2>
@@ -202,6 +188,14 @@ public class OneTimeTokenLoginConfigurerTests {
//@formatter:on
}
@Test
void oneTimeTokenWhenLoginPageConfiguredThenRedirects() throws Exception {
this.spring.register(OneTimeTokenLoginPageConfig.class).autowire();
this.mvc.perform(get("/login"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("http://localhost/custom-login"));
}
@Test
void oneTimeTokenWhenNoTokenGenerationSuccessHandlerThenException() {
assertThatException()
@@ -304,6 +298,34 @@ public class OneTimeTokenLoginConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
static class OneTimeTokenLoginPageConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
OneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.oneTimeTokenLogin((ott) -> ott
.tokenGenerationSuccessHandler(ottSuccessHandler)
.loginPage("/custom-login")
);
// @formatter:on
return http.build();
}
@Bean
TestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
return new TestOneTimeTokenGenerationSuccessHandler();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
@@ -321,7 +343,7 @@ public class OneTimeTokenLoginConfigurerTests {
.tokenGeneratingUrl("/generateurl")
.tokenGenerationSuccessHandler(ottSuccessHandler)
.loginProcessingUrl("/loginprocessingurl")
.authenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/authenticated"))
.successHandler(new SimpleUrlAuthenticationSuccessHandler("/authenticated"))
);
// @formatter:on
return http.build();
@@ -334,34 +356,6 @@ public class OneTimeTokenLoginConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
static class OneTimeTokenFormLoginConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
OneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.tokenGenerationSuccessHandler(ottSuccessHandler)
);
// @formatter:on
return http.build();
}
@Bean
TestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
return new TestOneTimeTokenGenerationSuccessHandler();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)

View File

@@ -250,6 +250,41 @@ public class OneTimeTokenLoginSpecTests {
// @formatter:on
}
@Test
void oneTimeTokenWhenConfiguredThenRendersRequestTokenForm() {
this.spring.register(OneTimeTokenDefaultConfig.class).autowire();
//@formatter:off
byte[] responseByteArray = this.client.mutateWith(SecurityMockServerConfigurers.csrf())
.get()
.uri((uriBuilder) -> uriBuilder
.path("/login")
.build()
)
.exchange()
.expectBody()
.returnResult()
.getResponseBody();
// @formatter:on
String response = new String(responseByteArray);
assertThat(response.contains(EXPECTED_HTML_HEAD)).isTrue();
assertThat(response.contains(GENERATE_OTT_PART)).isTrue();
}
@Test
void oneTimeTokenWhenConfiguredThenRedirectsToLoginPage() {
this.spring.register(OneTimeTokenDefaultConfig.class).autowire();
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
.get()
.uri((uriBuilder) -> uriBuilder.path("/").build())
.exchange()
.expectHeader()
.location("/login");
}
@Test
void oneTimeTokenWhenFormLoginConfiguredThenRendersRequestTokenForm() {
this.spring.register(OneTimeTokenFormLoginConfig.class).autowire();
@@ -280,6 +315,18 @@ public class OneTimeTokenLoginSpecTests {
return lastToken;
}
@Test
void oneTimeTokenWhenCustomLoginPageThenRedirects() {
this.spring.register(OneTimeTokenDifferentUrlsConfig.class).autowire();
this.client.mutateWith(SecurityMockServerConfigurers.csrf())
.get()
.uri((uriBuilder) -> uriBuilder.path("/login").build())
.exchange()
.expectHeader()
.location("/custom-login");
}
@Test
void oneTimeTokenWhenNoOneTimeTokenGenerationSuccessHandlerThenException() {
assertThatException()
@@ -362,6 +409,7 @@ public class OneTimeTokenLoginSpecTests {
.authenticated()
)
.oneTimeTokenLogin((ott) -> ott
.loginPage("/custom-login")
.tokenGeneratingUrl("/generateurl")
.tokenGenerationSuccessHandler(ottSuccessHandler)
.loginProcessingUrl("/loginprocessingurl")