Skip to content

Commit

Permalink
Reactive: one Time Token login registers the default login page
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Garnier-Moiroux <[email protected]>
  • Loading branch information
Kehrlann committed Feb 3, 2025
1 parent 2b4e3fb commit 3e87a99
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3035,7 +3035,8 @@ protected void configure(ServerHttpSecurity http) {
return;
}
if (http.formLogin != null && http.formLogin.isEntryPointExplicit
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)) {
|| http.oauth2Login != null && StringUtils.hasText(http.oauth2Login.loginPage)
|| http.oneTimeTokenLogin != null && StringUtils.hasText(http.oneTimeTokenLogin.loginPage)) {
return;
}
LoginPageGeneratingWebFilter loginPage = null;
Expand All @@ -3050,6 +3051,13 @@ protected void configure(ServerHttpSecurity http) {
}
loginPage.setOauth2AuthenticationUrlToClientName(urlToText);
}
if (http.oneTimeTokenLogin != null) {
if (loginPage == null) {
loginPage = new LoginPageGeneratingWebFilter();
}
loginPage.setOneTimeTokenEnabled(true);
loginPage.setGenerateOneTimeTokenUrl(http.oneTimeTokenLogin.tokenGeneratingUrl);
}
if (loginPage != null) {
http.addFilterAt(loginPage, SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
http.addFilterBefore(DefaultResourcesWebFilter.css(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);
Expand Down Expand Up @@ -5948,11 +5956,13 @@ public final class OneTimeTokenLoginSpec {

private boolean submitPageEnabled = true;

private String loginPage;

protected void configure(ServerHttpSecurity http) {
configureSubmitPage(http);
configureOttGenerateFilter(http);
configureOttAuthenticationFilter(http);
configureDefaultLoginPage(http);
configureDefaultEntryPoint(http);
}

private void configureOttAuthenticationFilter(ServerHttpSecurity http) {
Expand Down Expand Up @@ -5988,17 +5998,29 @@ private void configureOttGenerateFilter(ServerHttpSecurity http) {
http.addFilterAt(generateFilter, SecurityWebFiltersOrder.ONE_TIME_TOKEN);
}

private void configureDefaultLoginPage(ServerHttpSecurity http) {
if (http.formLogin != null) {
for (WebFilter webFilter : http.webFilters) {
OrderedWebFilter orderedWebFilter = (OrderedWebFilter) webFilter;
if (orderedWebFilter.webFilter instanceof LoginPageGeneratingWebFilter loginPageGeneratingFilter) {
loginPageGeneratingFilter.setOneTimeTokenEnabled(true);
loginPageGeneratingFilter.setGenerateOneTimeTokenUrl(this.tokenGeneratingUrl);
break;
}
private void configureDefaultEntryPoint(ServerHttpSecurity http) {
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
ServerWebExchangeMatcher xhrMatcher = (exchange) -> {
if (exchange.getRequest().getHeaders().getOrEmpty("X-Requested-With").contains("XMLHttpRequest")) {
return ServerWebExchangeMatcher.MatchResult.match();
}
return ServerWebExchangeMatcher.MatchResult.notMatch();
};
ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);
ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(notXhrMatcher,
htmlMatcher);
String loginPage = "/login";
if (this.loginPage != null) {
loginPage = this.loginPage;
}
RedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(
loginPage);
defaultEntryPoint.setRequestCache(http.requestCache.requestCache);
http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));

}

/**
Expand Down Expand Up @@ -6200,6 +6222,19 @@ Please provide it as a bean or pass it to the oneTimeTokenLogin() DSL.
return this.tokenGenerationSuccessHandler;
}

/**
* Specifies the URL to send users to if login is required. A default login page
* will be generated when this attribute is not specified.
* @param loginPage the URL to send users to if login is required
* @return the {@link OAuth2LoginSpec} for further configuration
* @since 6.5
*/
public OneTimeTokenLoginSpec loginPage(String loginPage) {
Assert.hasText(loginPage, "loginPage cannot be empty");
this.loginPage = loginPage;
return this;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,41 @@ void oneTimeTokenWhenWrongTokenThenAuthenticationFail() {
// @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();
Expand All @@ -268,6 +303,18 @@ void oneTimeTokenWhenFormLoginConfiguredThenRendersRequestTokenForm() {
assertThat(response.contains(GENERATE_OTT_PART)).isTrue();
}

@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()
Expand Down Expand Up @@ -318,6 +365,7 @@ SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
.authenticated()
)
.oneTimeTokenLogin((ott) -> ott
.loginPage("/custom-login")
.tokenGeneratingUrl("/generateurl")
.tokenGenerationSuccessHandler(new TestServerOneTimeTokenGenerationSuccessHandler("/redirected"))
.loginProcessingUrl("/loginprocessingurl")
Expand Down

0 comments on commit 3e87a99

Please sign in to comment.