Skip to content

Improve rate-limit UX: calm error message and hide resend button during lockout#921

Open
dknauss wants to merge 3 commits into
WordPress:masterfrom
dknauss:ux/rate-limit-messaging
Open

Improve rate-limit UX: calm error message and hide resend button during lockout#921
dknauss wants to merge 3 commits into
WordPress:masterfrom
dknauss:ux/rate-limit-messaging

Conversation

@dknauss

@dknauss dknauss commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Companion to PR #917. Addresses #918 and #920.

What changed

Error message wording (class-two-factor-core.php)

Before:

ERROR: Too many invalid verification codes, you can try again in %s. This limit protects your account against automated attacks.

After:

Too many incorrect verification codes. Please wait %s and reload this page to try again.

The old message used an ERROR: severity prefix appropriate for system failures, not a temporary delay. "Automated attacks" frames a lockout caused by mistyped codes as a security incident, which is alarming and inaccurate for the legitimate user almost always reading it. The new message is calm, drops the attack framing, and tells the user what to do (wait and reload).

Resend Code button (providers/class-two-factor-email.php)

authentication_page() now checks Two_Factor_Core::is_user_rate_limited() before rendering the resend button and the "A verification code has been sent" prompt. While rate-limited, both are suppressed.

Showing an interactive resend button during a lockout implied it would work. Clicking it only returned the same rate-limit error. The button reappears normally once the lockout expires.

The rate-limit check in authentication_page() also prevents token regeneration while locked out — a user without a token will not receive a new email until the lockout expires and they reload.

Tests

Three new tests, all passing against the full suite (192 tests):

  • test_rate_limit_error_message_is_calm_and_actionable — asserts no ERROR: prefix and no "automated attacks"
  • test_authentication_page_hides_resend_button_when_rate_limited — asserts INPUT_NAME_RESEND_CODE absent from output
  • test_authentication_page_does_not_send_email_when_rate_limited_and_no_token — asserts no email sent when called without a token during lockout

Not addressed here

Issue #919 (the "if this wasn't you, reset your password" warning that addresses two mutually exclusive audiences) is a separate concern affecting all providers and will be a separate PR.

Closes #918
Closes #920

🤖 Assisted by Claude Code

Try in Playground

Open WordPress Playground Preview

What you'll see: The 2FA email code entry form showing the new rate-limit error message — no ERROR: prefix, no "automated attacks" framing. The Resend Code button and "A verification code has been sent" notice are both absent (suppressed while locked out).

What to test manually:

  • Compare the error message text to the "Before" in this PR
  • Confirm the Resend Code button is not rendered
  • The message tells you how long to wait — reload the page after that window passes and the Resend button should reappear

Technical: The demo installs the plugin directly from this PR branch (dknauss/two-factor@ux/rate-limit-messaging) via a GitHub archive zip through the Playground CORS proxy — you're seeing the live patch code. A mu-plugin shim creates a demo user with Email 2FA enabled, records 4 failed attempts with an active rate limit, and renders the login form at /wp-login.php?action=two_factor_demo without going through a real login flow.

dependabot Bot and others added 2 commits July 2, 2026 23:36
Bumps the npm_and_yarn group with 1 update in the / directory: [basic-ftp](https://github.com/patrickjuchli/basic-ftp).


Updates `basic-ftp` from 5.2.2 to 5.3.0
- [Release notes](https://github.com/patrickjuchli/basic-ftp/releases)
- [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md)
- [Commits](patrickjuchli/basic-ftp@v5.2.2...v5.3.0)

---
updated-dependencies:
- dependency-name: basic-ftp
  dependency-version: 5.3.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…n during lockout

Addresses WordPress#918 and WordPress#920.

The rate-limit error message previously read "ERROR: Too many invalid
verification codes... This limit protects your account against automated
attacks." For a legitimate user who mistyped their code, this is alarming
and inaccurate. Replaced with a calm, actionable message that tells the
user to wait and reload.

The email provider's authentication_page() now checks is_user_rate_limited()
before rendering the Resend Code button or the "A verification code has been
sent" prompt. Showing an interactive resend button during a lockout misled
users into thinking it would work; clicking it only returned the rate-limit
error again. The button is hidden until the lockout expires.

The rate-limit check in authentication_page() also prevents token regeneration
while locked out, which complements the existing test added in the rate-limit
gate PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: dknauss <dpknauss@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Seeded demo: 4 failed verification attempts 1 second ago,
shows the rate-limit error message in context.

Landing page: /wp-login.php?action=two_factor_demo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant