Repository navigation
Keep revalidation on the current site when the login URL is filtered - #1029
Conversation
Revalidation confirms a user who is already logged in to this site, often from an iframe, so its links no longer follow the login URL filter to another host. Logins still go through the filtered login URL.
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved review issues remain, and all reviewed changes have regression coverage.
No open issues 🎉
What changed in this PR
Updates 2FA URL generation so revalidation stays on the current site while normal login URLs honor login_url filters.
Changes:
- Uses
site_url()for revalidation URLs. - Preserves filtered login behavior for other actions.
- Adds regression tests for both flows.
| File | Description |
|---|---|
tests/class-two-factor-core.php |
Tests filtered login and current-site revalidation behavior. |
class-two-factor-core.php |
Separates revalidation URL generation from normal login URLs. |
Review effort: Lite
|
Thank you @obenland and sorry for the trouble! We'll take a look at it asap and get it merged. |
What?
Follow-up to #1000. Revalidation URLs from
Two_Factor_Core::login_url()are built from the current site'swp-login.phpagain. All other login URLs keep going throughwp_login_url().Why?
#1000 switched
login_url()towp_login_url()so that plugins filteringlogin_urlcan rewrite the 2FA URLs (#885). That's right for logging in, but revalidation isn't a login: it confirms a user who is already logged in on the current site, often from an iframe modal.Sites that move logins to a separate host broke after #1000. WordPress.org, for example, sends every login to login.wordpress.org. The revalidation iframe on other subdomains now loads login.wordpress.org, which is blocked by core's
frame-ancestors 'self'header fromsend_frame_options_header(). Even when the page loads, the form posts to a host whose cookies don't belong to the current site's session.See https://wordpress.slack.com/archives/C02QB8GMM/p1792530424151129.
How?
login_url()usessite_url( 'wp-login.php?action=revalidate_2fa', $scheme )when the action isrevalidate_2fa, andwp_login_url()otherwise.add_query_arg(). Revalidation URLs therefore match what they were before Use wp_login_url() for login URLs and defer the redirect decision to core logic #1000, e.g.wp-login.php?action=revalidate_2fa&interim-login=1.site_url()path, as in core's ownwp-login.php?action=…calls. That letssite_urlfilters tell the revalidation form apart from the login form.login_urlto another host. It checks thatvalidate_2fafollows the filter, whilerevalidate_2fastays on the site URL and keeps its other params.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5.5
Used for: Investigating the regression, implementation, and test; reviewed by me.
Testing Instructions
npm run env start, thennpm test. All tests should pass.login_urlfilter that returns a URL on another host.wp-login.php?action=revalidate_2fa.Changelog Entry
🤖 Generated with Claude Code