From 036ccff1f5aa036c16170a8340db40f7e83e5070 Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Tue, 7 Apr 2026 18:18:57 -0600 Subject: [PATCH] Move Focus to OTT Button When Username is Read-Only Closes gh-18817 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com> --- .../ott/OneTimeTokenLoginConfigurerTests.java | 4 +- .../ui/DefaultLoginPageGeneratingFilter.java | 6 +- .../ui/LoginPageGeneratingWebFilter.java | 2 +- ...DefaultLoginPageGeneratingFilterTests.java | 65 ++++++++++--------- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java index 5d21957d97..e5dd999525 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java @@ -174,10 +174,10 @@ public class OneTimeTokenLoginConfigurerTests {

- +

- + diff --git a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java index 45f2ce23c2..d570bb2738 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java @@ -366,6 +366,7 @@ public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { String usernameInput = (username != null) ? HtmlTemplates.fromTemplate(ONE_TIME_READONLY_USERNAME_INPUT).withValue("username", username).render() : ONE_TIME_USERNAME_INPUT; + String buttonAutofocus = (username != null) ? " autofocus" : ""; return HtmlTemplates.fromTemplate(ONE_TIME_TEMPLATE) .withValue("generateOneTimeTokenUrl", contextPath + this.generateOneTimeTokenUrl) @@ -373,6 +374,7 @@ public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { .withRawHtml("logoutMessage", renderSuccess(logoutSuccess)) .withRawHtml("hiddenInputs", hiddenInputs) .withRawHtml("usernameInput", usernameInput) + .withRawHtml("buttonAutofocus", buttonAutofocus) .render(); } @@ -604,7 +606,7 @@ public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { {{usernameInput}}

{{hiddenInputs}} - + """; @@ -613,7 +615,7 @@ public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { """; private static final String ONE_TIME_USERNAME_INPUT = """ - + """; } diff --git a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java index f9891fe0f8..fc6df6c9a4 100644 --- a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java @@ -252,7 +252,7 @@ public class LoginPageGeneratingWebFilter implements WebFilter { {{errorMessage}}{{logoutMessage}}

- +

{{csrf}} diff --git a/web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java index dd6344ec82..faef2457c7 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java @@ -190,18 +190,19 @@ public class DefaultLoginPageGeneratingFilterTests { MockHttpServletResponse response = new MockHttpServletResponse(); filter.doFilter(new MockHttpServletRequest("GET", "/login"), response, this.chain); assertThat(response.getContentAsString()).contains("Request a One-Time Token"); - assertThat(response.getContentAsString()).contains(""" -
-

Request a One-Time Token

+ assertThat(response.getContentAsString()).contains( + """ + +

Request a One-Time Token

-

- - -

+

+ + +

- -
- """); + + + """); } @Test @@ -216,18 +217,19 @@ public class DefaultLoginPageGeneratingFilterTests { FactorGrantedAuthority.OTT_AUTHORITY); filter.doFilter(loginRequest, response, this.chain); assertThat(response.getContentAsString()).contains("Request a One-Time Token"); - assertThat(response.getContentAsString()).contains(""" -
-

Request a One-Time Token

+ assertThat(response.getContentAsString()).contains( + """ + +

Request a One-Time Token

-

- - -

+

+ + +

- -
- """); + + + """); assertThat(response.getContentAsString()).doesNotContain("Password"); } @@ -245,18 +247,19 @@ public class DefaultLoginPageGeneratingFilterTests { .get("/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing") .build(), response, this.chain); assertThat(response.getContentAsString()).contains("Request a One-Time Token"); - assertThat(response.getContentAsString()).contains(""" -
-

Request a One-Time Token

+ assertThat(response.getContentAsString()).contains( + """ + +

Request a One-Time Token

-

- - -

+

+ + +

- -
- """); + + + """); assertThat(response.getContentAsString()).contains("Password"); } @@ -297,6 +300,8 @@ public class DefaultLoginPageGeneratingFilterTests { """ """); + assertThat(response.getContentAsString()).contains(""" + """); assertThat(response.getContentAsString()).contains(""" """);