Repository navigation
Login and Registration: Add support for /.well-known/change-password - #12621
HasnainAshfaq wants to merge 13 commits into
Conversation
Adds a redirect from `/.well-known/change-password` to the user profile page (`wp-admin/profile.php`) inside `wp_redirect_admin_locations()`. Browsers and password managers use this well-known URL to help users change their passwords. A `wp_change_password_url` filter is provided for sites with custom user flows (e.g. membership plugins) to redirect to a different URL. Fixes #51173
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
There was a problem hiding this comment.
Pull request overview
Adds support for the W3C /.well-known/change-password endpoint by redirecting requests to WordPress’s password-change flow, plus PHPUnit coverage for the new redirect behavior.
Changes:
- Add
/.well-known/change-passwordhandling towp_redirect_admin_locations()with a newwp_change_password_urlfilter. - Redirect well-known change-password requests to
admin_url( 'profile.php' ). - Add a new canonical test file covering redirect behavior, permalink requirements, and the filter.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
src/wp-includes/canonical.php |
Adds the well-known change-password redirect and introduces a filter for customizing the redirect target. |
tests/phpunit/tests/canonical/changePassword.php |
Adds PHPUnit tests intended to validate the redirect behavior and filter integration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $password_change_urls = array( | ||
| home_url( '.well-known/change-password', 'relative' ), | ||
| site_url( '.well-known/change-password', 'relative' ), | ||
| ); |
| public function test_well_known_change_password_with_trailing_slash_does_not_redirect() { | ||
| $redirect = $this->get_redirect_for( '/.well-known/change-password/' ); | ||
| $this->assertNull( $redirect, 'Trailing slash variant should not redirect (untrailingslashit handles exact match only).' ); | ||
| } |
| add_filter( 'wp_change_password_url', static fn() => $custom_url ); | ||
| $redirect = $this->get_redirect_for( '/.well-known/change-password' ); | ||
| remove_all_filters( 'wp_change_password_url' ); | ||
|
|
- Add root-relative `/.well-known/change-password` to the match array so subdirectory installs (e.g. example.com/blog/) are also covered - Fix test helper to use exception interception instead of empty-string filter return, preventing premature exit() from killing PHPUnit - Correct trailing-slash test: untrailingslashit() normalises the URI so it does match and redirect (test now asserts the redirect fires) - Use remove_filter() instead of remove_all_filters() in filter test Fixes #51173
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
tests/phpunit/tests/canonical/changePassword.php:43
get_redirect_for()forces$wp_query->is_404 = falseinfinally, which may clobber the previous value if the caller/test had set it differently. Store the original value and restore it to keep test state isolated.
global $wp_query;
$wp_query->is_404 = true;
| /** | ||
| * Original REQUEST_URI value. | ||
| * | ||
| * @var string | ||
| */ | ||
| private $original_request_uri; | ||
|
|
||
| public function set_up() { | ||
| parent::set_up(); | ||
| $this->original_request_uri = $_SERVER['REQUEST_URI'] ?? ''; | ||
| $this->set_permalink_structure( '/%postname%/' ); | ||
| } | ||
|
|
||
| public function tear_down() { | ||
| $_SERVER['REQUEST_URI'] = $this->original_request_uri; | ||
| parent::tear_down(); | ||
| } |
| $redirect = $this->get_redirect_for( '/.well-known/change-password' ); | ||
| $this->assertStringContainsString( 'profile.php', $redirect, 'Should redirect to the profile page.' ); |
| if ( in_array( untrailingslashit( $_SERVER['REQUEST_URI'] ), $password_change_urls, true ) ) { | ||
| /** | ||
| * Filters the URL to redirect to when a browser or password manager | ||
| * requests the well-known change-password URL (/.well-known/change-password). | ||
| * | ||
| * @since 6.9.0 | ||
| * | ||
| * @param string $url The URL to redirect to. Default is the user profile page. | ||
| */ | ||
| wp_redirect( apply_filters( 'wp_change_password_url', admin_url( 'profile.php' ) ) ); | ||
| exit; |
| $redirect = $this->get_redirect_for( '/.well-known/change-password/' ); | ||
| $this->assertStringContainsString( 'profile.php', $redirect, 'Trailing slash variant should also redirect to the profile page.' ); |
Aligns $filter assignment with surrounding variables per WPCS rules. Fixes #51173
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (5)
tests/phpunit/tests/canonical/changePassword.php:17
- The test stores
$_SERVER['REQUEST_URI']as an empty string when the key is unset, and then always writes it back intear_down(). This can change global state for other tests (key becomes set when it previously wasn’t). Preserve the original “unset vs set” state and restore accordingly.
/**
* Original REQUEST_URI value.
*
* @var string
*/
tests/phpunit/tests/canonical/changePassword.php:45
get_redirect_for()forces$wp_query->is_404tofalsein thefinallyblock, which may not restore the prior value if it was alreadytrue(or otherwise modified) before the helper ran. Back up the original value and restore it.
global $wp_query;
$wp_query->is_404 = true;
$captured = null;
tests/phpunit/tests/canonical/changePassword.php:70
- If no redirect fires,
$redirectwill benullandassertStringContainsString()can error (type mismatch) instead of producing a clear test failure. Add an explicit non-null assertion first for better failure output.
public function test_well_known_change_password_redirects_to_profile() {
$redirect = $this->get_redirect_for( '/.well-known/change-password' );
$this->assertStringContainsString( 'profile.php', $redirect, 'Should redirect to the profile page.' );
}
tests/phpunit/tests/canonical/changePassword.php:78
- If no redirect fires,
$redirectwill benullandassertStringContainsString()can error (type mismatch) instead of producing a clear test failure. Add an explicit non-null assertion first for better failure output.
public function test_well_known_change_password_with_trailing_slash_redirects_to_profile() {
$redirect = $this->get_redirect_for( '/.well-known/change-password/' );
$this->assertStringContainsString( 'profile.php', $redirect, 'Trailing slash variant should also redirect to the profile page.' );
}
src/wp-includes/canonical.php:1081
- The PR description says the trailing-slash variant should not redirect, but the implementation uses
untrailingslashit( $_SERVER['REQUEST_URI'] ), which will match both/.well-known/change-passwordand/.well-known/change-password/. Please align either the description or the redirect matching logic/tests so they reflect the intended behavior.
if ( in_array( untrailingslashit( $_SERVER['REQUEST_URI'] ), $password_change_urls, true ) ) {
/**
* Filters the URL to redirect to when a browser or password manager
* requests the well-known change-password URL (/.well-known/change-password).
*
- Restore REQUEST_URI to truly unset state if it was not set before the test ran, rather than forcing it to an empty string - Save and restore the original $wp_query->is_404 value in the helper instead of hard-coding false, preventing state leakage between tests - Add assertNotNull() before assertStringContainsString() calls so a missing redirect produces a clear failure rather than a type error Fixes #51173
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
tests/phpunit/tests/canonical/changePassword.php:93
- PR description says the trailing-slash variant (
/.well-known/change-password/) "does not redirect", but both the implementation and this test expect it to redirect due tountrailingslashit( $_SERVER['REQUEST_URI'] ). Please align the intended behavior across the PR description + implementation + tests (either update the description, or change the matching logic/tests if the trailing-slash variant should truly not redirect).
public function test_well_known_change_password_with_trailing_slash_redirects_to_profile() {
$redirect = $this->get_redirect_for( '/.well-known/change-password/' );
$this->assertNotNull( $redirect, 'A redirect should fire for the trailing-slash variant.' );
$this->assertStringContainsString( 'profile.php', $redirect, 'Trailing slash variant should also redirect to the profile page.' );
}
|
@HasnainAshfaq i've come across the PR. could you please check the open copilot feedback and fix/resolve all open comments - if valid? i'd like to check this and see if it could be helpful to push this further. |
| * Filters the URL to redirect to when a browser or password manager | ||
| * requests the well-known change-password URL (/.well-known/change-password). | ||
| * | ||
| * @since 6.9.0 |
There was a problem hiding this comment.
| * @since 6.9.0 | |
| * @since 7.2.0 |
…SInCE tag. - Match only the spec-defined root path '/.well-known/change-password' directly, removing the home_url() and site_url() relative variants. Per the W3C spec the well-known URL is always at the origin root, so the home/site-relative paths were incorrect for subdirectory installs and unnecessary for root installs. - Update @SInCE tag to 7.2.0 per committer suggestion.
|
Thanks for the review @masteradhoc! I've gone through all the open Copilot comments:
|
peterwilsoncc
left a comment
There was a problem hiding this comment.
I've added a suggestion inline to use wp_safe_redirect().
|
I've taken the liberty of pushing the following:
|
Introduce support for the [https://w3c.github.io/webappsec-change-password-url/ Well-Known URL for Changing Passwords], a standardized URL for tools such as password managers to make change of password forms discoverable. Per the specification this is only available to WordPress sites installed in the root directory of a domain. The URL `example.com/.well-known/change-password` will redirect a user to their profile page. Introduces the filter `wp_change_password_url` to allow sites using a non-standard location for managing user passwords. If this is hosted on an another host, the host will need to be added to the allow list via the `allowed_redirect_hosts` filter in order to pass the `wp_safe_redirect()` validation process. Developed in #12621 Props audrasjb, ayeshrajans, hasnainashfaq, jns141191, johnbillion, masteradhoc, pfefferle, romainmrhenry, sajib1223, sergeybiryukov, swissspidy. Fixes #51173. git-svn-id: https://develop.svn.wordpress.org/trunk@64348 602fd350-edb4-49c9-b593-d223f7449a82
Introduce support for the [https://w3c.github.io/webappsec-change-password-url/ Well-Known URL for Changing Passwords], a standardized URL for tools such as password managers to make change of password forms discoverable. Per the specification this is only available to WordPress sites installed in the root directory of a domain. The URL `example.com/.well-known/change-password` will redirect a user to their profile page. Introduces the filter `wp_change_password_url` to allow sites using a non-standard location for managing user passwords. If this is hosted on an another host, the host will need to be added to the allow list via the `allowed_redirect_hosts` filter in order to pass the `wp_safe_redirect()` validation process. Developed in WordPress/wordpress-develop#12621 Props audrasjb, ayeshrajans, hasnainashfaq, jns141191, johnbillion, masteradhoc, pfefferle, romainmrhenry, sajib1223, sergeybiryukov, swissspidy. Fixes #51173. Built from https://develop.svn.wordpress.org/trunk@64348 git-svn-id: http://core.svn.wordpress.org/trunk@63498 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Fixes https://core.trac.wordpress.org/ticket/51173
What
Adds a redirect from
/.well-known/change-passwordtowp-admin/profile.phpinside the existingwp_redirect_admin_locations()function incanonical.php. This follows the W3C Well-Known Change Password URL spec.Why
Browsers (Chrome, Safari, Firefox) and password managers use
/.well-known/change-passwordto automatically navigate users to the password-change page. Without this redirect, WordPress sites return a 404 for this URL, breaking password manager integrations.How
/.well-known/change-passworddetection towp_redirect_admin_locations(), mirroring the existing pattern for/loginand/adminaliases.admin_url( 'profile.php' )— the profile page handles unauthenticated users by redirecting them to the login page withredirect_toset back to the profile page.wp_change_password_urlfilter allows sites with custom user flows (membership plugins, WooCommerce accounts) to override the redirect target.Tests
Adds
tests/phpunit/tests/canonical/changePassword.phpcovering:/.well-known/change-passworduntrailingslashitbehavior elsewhere)wp_change_password_urlfilter is respected