Captcha on login and password reset forms (login hooks + single captcha setting) - #3765
Draft
dparker1005 wants to merge 2 commits into
Draft
Captcha on login and password reset forms (login hooks + single captcha setting)#3765dparker1005 wants to merge 2 commits into
dparker1005 wants to merge 2 commits into
Conversation
- New pmpro_login_form_before_submit_button action inside the PMPro login form via a temporary login_form_middle filter. - New pmpro_authenticate_login_checks filter on authenticate (priority 40), scoped to PMPro login form and wp-login.php submissions only so other login flows (XML-RPC, other plugins' forms, wp_signon calls) are never affected. - New pmpro_login_forms_handler_message/msgt filters so custom error codes passed back to the login page can display messages. - pmpro_authenticate_username_password() now only redirects for the empty_username/empty_password codes that WP core never fires wp_login_failed for, and exits after redirecting. All other failures redirect through pmpro_login_failed() as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etting - New Captcha setting on the Security Settings page replaces the separate "Use reCAPTCHA?" and "Use CloudFlare Turnstile?" settings. Legacy options are still read when the new setting has never been saved and are kept in sync on save for backwards compatibility. - New includes/captcha.php with shared captcha service code: a pmpro_captcha_services registry filter so captcha services (including future add ons) register themselves from their own files, pmpro_captcha() to get the active service, and the shared failed-attempt gate that phase 3 can repoint at the spam activity table. - reCAPTCHA and Turnstile now show on the PMPro and wp-login.php login and lost password forms, but only after a failed login attempt is tracked for the visitor IP (and only when keys are configured), preventing lockouts from misconfigured captchas. - Failed logins are now tracked when a captcha is enabled, even if spam protection is off. - Fixed Turnstile checkout validation marking the session as validated even when verification failed, which let a failed captcha be bypassed on retry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phases 1 and 2 of the captcha-on-login work discussed for 3.9 (replaces the approach in #3493; phase 3 is the spam activity table in #3640).
Phase 1: Generic login flow hooks (commit 1,
includes/login.phponly)Gives captchas (and anything else) clean hook points in the login flow, mirroring how checkout isolates captcha code:
pmpro_login_form_before_submit_button— fires inside the PMPro login form, via alogin_form_middlefilter added/removed aroundwp_login_form()(same pattern as the existing hidden-field filter), so it only ever runs on PMPro's forms.pmpro_authenticate_login_checks— filter onauthenticateat priority 40, gated to PMPro-form submissions (pmpro_login_form_used) or real wp-login.php login submissions (did_action( 'login_form_login' )). XML-RPC, other plugins' login forms, and programmaticwp_signon()calls are never touched, so a captcha can't lock out a flow that didn't display it.pmpro_login_forms_handler_message/pmpro_login_forms_handler_msgt— lets custom error codes passed back to the login page display messages.pmpro_authenticate_username_password()now only redirects forempty_username/empty_password(the codes WP core never fireswp_login_failedfor) and exits after redirecting. All other failures flow throughpmpro_login_failed()— previously both functions calledwp_redirect()in the same request and the second one happened to win.Phase 2: Captchas use the hooks (commit 2)
pmpro_captcha_servicesfilter inincludes/captcha.php. reCAPTCHA and Turnstile register themselves from their own files, and a future captcha add on can drop in the same way with no core changes. If a registered service disappears (add on deactivated), the site fails safe to no captcha.Testing done
Full curl-based pass on a local site: hook plumbing proven with a throwaway consumer; login/logout/lost password/reset password behavior verified unchanged with captcha off; with dummy keys, challenge lifecycle verified for both services on all four forms (no captcha before a failure, widget after, correct-password-but-no-token rejected with a displayed error, lost password blocked without sending email); checkout gating verified for both services incl. legacy-option derivation; registry verified with a fake third-party service (registers, saves, fails safe when removed).
Still needs a browser pass with real keys: actual token round trips (only the empty-token rejection path is testable with dummy keys), the v3-invisible inline JS, and the settings show/hide toggling.
🤖 Generated with Claude Code