Skip to content

Commit 036ccff

Browse files
committed
Move Focus to OTT Button When Username is Read-Only
Closes gh-18817 Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
1 parent 245733a commit 036ccff

4 files changed

Lines changed: 42 additions & 35 deletions

File tree

config/src/test/java/org/springframework/security/config/annotation/web/configurers/ott/OneTimeTokenLoginConfigurerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ void oneTimeTokenWhenConfiguredThenRendersRequestTokenForm() throws Exception {
174174
175175
<p>
176176
<label for="ott-username" class="screenreader">Username</label>
177-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
177+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
178178
</p>
179179
<input name="_csrf" type="hidden" value="%s" />
180-
<button class="primary" type="submit">Send Token</button>
180+
<button class="primary" type="submit" form="ott-form">Send Token</button>
181181
</form>
182182
183183

web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,15 @@ private String renderOneTimeTokenLogin(HttpServletRequest request, boolean login
366366
String usernameInput = (username != null)
367367
? HtmlTemplates.fromTemplate(ONE_TIME_READONLY_USERNAME_INPUT).withValue("username", username).render()
368368
: ONE_TIME_USERNAME_INPUT;
369+
String buttonAutofocus = (username != null) ? " autofocus" : "";
369370

370371
return HtmlTemplates.fromTemplate(ONE_TIME_TEMPLATE)
371372
.withValue("generateOneTimeTokenUrl", contextPath + this.generateOneTimeTokenUrl)
372373
.withRawHtml("errorMessage", renderError(loginError, errorMsg))
373374
.withRawHtml("logoutMessage", renderSuccess(logoutSuccess))
374375
.withRawHtml("hiddenInputs", hiddenInputs)
375376
.withRawHtml("usernameInput", usernameInput)
377+
.withRawHtml("buttonAutofocus", buttonAutofocus)
376378
.render();
377379
}
378380

@@ -604,7 +606,7 @@ private boolean matches(HttpServletRequest request, @Nullable String url) {
604606
{{usernameInput}}
605607
</p>
606608
{{hiddenInputs}}
607-
<button class="primary" type="submit">Send Token</button>
609+
<button class="primary" type="submit" form="ott-form"{{buttonAutofocus}}>Send Token</button>
608610
</form>
609611
""";
610612

@@ -613,7 +615,7 @@ private boolean matches(HttpServletRequest request, @Nullable String url) {
613615
""";
614616

615617
private static final String ONE_TIME_USERNAME_INPUT = """
616-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
618+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
617619
""";
618620

619621
}

web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ private static String createLogoutSuccess(boolean isLogoutSuccess) {
252252
{{errorMessage}}{{logoutMessage}}
253253
<p>
254254
<label for="ott-username" class="screenreader">Username</label>
255-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
255+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
256256
</p>
257257
{{csrf}}
258258
<button class="primary" type="submit" form="ott-form">Send Token</button>

web/src/test/java/org/springframework/security/web/authentication/DefaultLoginPageGeneratingFilterTests.java

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -190,18 +190,19 @@ public void generateWhenOneTimeTokenLoginThenOttForm() throws Exception {
190190
MockHttpServletResponse response = new MockHttpServletResponse();
191191
filter.doFilter(new MockHttpServletRequest("GET", "/login"), response, this.chain);
192192
assertThat(response.getContentAsString()).contains("Request a One-Time Token");
193-
assertThat(response.getContentAsString()).contains("""
194-
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
195-
<h2>Request a One-Time Token</h2>
193+
assertThat(response.getContentAsString()).contains(
194+
"""
195+
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
196+
<h2>Request a One-Time Token</h2>
196197
197-
<p>
198-
<label for="ott-username" class="screenreader">Username</label>
199-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
200-
</p>
198+
<p>
199+
<label for="ott-username" class="screenreader">Username</label>
200+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
201+
</p>
201202
202-
<button class="primary" type="submit">Send Token</button>
203-
</form>
204-
""");
203+
<button class="primary" type="submit" form="ott-form">Send Token</button>
204+
</form>
205+
""");
205206
}
206207

207208
@Test
@@ -216,18 +217,19 @@ public void generateWhenOneTimeTokenRequestedThenOttForm() throws Exception {
216217
FactorGrantedAuthority.OTT_AUTHORITY);
217218
filter.doFilter(loginRequest, response, this.chain);
218219
assertThat(response.getContentAsString()).contains("Request a One-Time Token");
219-
assertThat(response.getContentAsString()).contains("""
220-
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
221-
<h2>Request a One-Time Token</h2>
220+
assertThat(response.getContentAsString()).contains(
221+
"""
222+
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
223+
<h2>Request a One-Time Token</h2>
222224
223-
<p>
224-
<label for="ott-username" class="screenreader">Username</label>
225-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
226-
</p>
225+
<p>
226+
<label for="ott-username" class="screenreader">Username</label>
227+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
228+
</p>
227229
228-
<button class="primary" type="submit">Send Token</button>
229-
</form>
230-
""");
230+
<button class="primary" type="submit" form="ott-form">Send Token</button>
231+
</form>
232+
""");
231233
assertThat(response.getContentAsString()).doesNotContain("Password");
232234
}
233235

@@ -245,18 +247,19 @@ public void generateWhenTwoAuthoritiesRequestedThenBothForms() throws Exception
245247
.get("/login?factor.type=ott&factor.type=password&factor.reason=missing&factor.reason=missing")
246248
.build(), response, this.chain);
247249
assertThat(response.getContentAsString()).contains("Request a One-Time Token");
248-
assertThat(response.getContentAsString()).contains("""
249-
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
250-
<h2>Request a One-Time Token</h2>
250+
assertThat(response.getContentAsString()).contains(
251+
"""
252+
<form id="ott-form" class="login-form" method="post" action="/ott/authenticate">
253+
<h2>Request a One-Time Token</h2>
251254
252-
<p>
253-
<label for="ott-username" class="screenreader">Username</label>
254-
<input type="text" id="ott-username" name="username" placeholder="Username" required>
255-
</p>
255+
<p>
256+
<label for="ott-username" class="screenreader">Username</label>
257+
<input type="text" id="ott-username" name="username" placeholder="Username" required autofocus>
258+
</p>
256259
257-
<button class="primary" type="submit">Send Token</button>
258-
</form>
259-
""");
260+
<button class="primary" type="submit" form="ott-form">Send Token</button>
261+
</form>
262+
""");
260263
assertThat(response.getContentAsString()).contains("Password");
261264
}
262265

@@ -297,6 +300,8 @@ public void generateWhenAuthenticatedThenReadOnlyUsername() throws Exception {
297300
"""
298301
<input type="text" id="ott-username" name="username" value="user" placeholder="Username" required readonly>
299302
""");
303+
assertThat(response.getContentAsString()).contains("""
304+
<button class="primary" type="submit" form="ott-form" autofocus>Send Token</button>""");
300305
assertThat(response.getContentAsString()).contains("""
301306
<input type="text" id="username" name="username" value="user" placeholder="Username" required readonly>
302307
""");

0 commit comments

Comments
 (0)