Users: Retain the current session when a user changes their password.

Prior to this change a new session was unnecessarily created when a user changed their own password.

Existing authentication cookies for the user will still be invalidated regardless of whether they share the same session token because session cookie keys contain a substring of the password hash.

Props snicco, narenin, johnbillion

Fixes #61366


git-svn-id: https://develop.svn.wordpress.org/trunk@59633 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
John Blackbourn 2025-01-16 15:33:31 +00:00
parent 8aea1ba5ee
commit 964c5d3857
2 changed files with 80 additions and 3 deletions

View File

@ -2780,8 +2780,6 @@ All at ###SITENAME###
$current_user = wp_get_current_user();
if ( $current_user->ID === $user_id ) {
if ( isset( $plaintext_pass ) ) {
wp_clear_auth_cookie();
/*
* Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
* If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
@ -2790,13 +2788,20 @@ All at ###SITENAME###
/** This filter is documented in wp-includes/pluggable.php */
$default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $user_id, false );
wp_clear_auth_cookie();
$remember = false;
$token = '';
if ( false !== $logged_in_cookie ) {
$token = $logged_in_cookie['token'];
}
if ( false !== $logged_in_cookie && ( (int) $logged_in_cookie['expiration'] - time() ) > $default_cookie_life ) {
$remember = true;
}
wp_set_auth_cookie( $user_id, $remember );
wp_set_auth_cookie( $user_id, $remember, '', $token );
}
}

View File

@ -63,9 +63,37 @@ class Tests_User extends WP_UnitTestCase {
public function set_up() {
parent::set_up();
add_action( 'set_auth_cookie', array( $this, 'action_set_auth_cookie' ), 10, 6 );
add_action( 'set_logged_in_cookie', array( $this, 'action_set_logged_in_cookie' ), 10 );
add_action( 'clear_auth_cookie', array( $this, 'action_clear_auth_cookie' ) );
$_COOKIE = array();
$this->author = clone self::$_author;
}
final public function action_set_auth_cookie(
string $cookie,
int $expire,
int $expiration,
int $user_id,
string $scheme,
string $token
): void {
$_COOKIE[ SECURE_AUTH_COOKIE ] = $cookie;
$_COOKIE[ AUTH_COOKIE ] = $cookie;
}
final public function action_set_logged_in_cookie( string $cookie ): void {
$_COOKIE[ LOGGED_IN_COOKIE ] = $cookie;
}
final public function action_clear_auth_cookie(): void {
unset( $_COOKIE[ LOGGED_IN_COOKIE ] );
unset( $_COOKIE[ SECURE_AUTH_COOKIE ] );
unset( $_COOKIE[ AUTH_COOKIE ] );
}
public function test_get_users_of_blog() {
// Add one of each user role.
$nusers = array(
@ -1122,6 +1150,50 @@ class Tests_User extends WP_UnitTestCase {
$this->assertEmpty( $user->user_activation_key );
}
/**
* @ticket 61366
* @dataProvider data_remember_user
*/
public function test_changing_own_password_retains_current_session( bool $remember ) {
$user = $this->author;
$manager = WP_Session_Tokens::get_instance( $user->ID );
$expiry = $remember ? ( 2 * WEEK_IN_SECONDS ) : ( 2 * DAY_IN_SECONDS );
$token = $manager->create( time() + $expiry );
$pass = $user->user_pass;
wp_set_current_user( $user->ID );
wp_set_auth_cookie( $user->ID, $remember, '', $token );
$cookie = $_COOKIE[ AUTH_COOKIE ];
$userdata = array(
'ID' => $user->ID,
'user_pass' => 'my_new_password',
);
$updated = wp_update_user( $userdata, $manager );
$parsed = wp_parse_auth_cookie();
// Check the prerequisites:
$this->assertNotWPError( $updated );
$this->assertNotSame( $pass, get_userdata( $user->ID )->user_pass );
// Check the session token:
$this->assertSame( $token, $parsed['token'] );
$this->assertCount( 1, $manager->get_all() );
// Check that the newly set auth cookie is valid:
$this->assertSame( $user->ID, wp_validate_auth_cookie() );
// Check that, despite the session token reuse, the old auth cookie should now be invalid because the password changed:
$this->assertFalse( wp_validate_auth_cookie( $cookie ) );
}
public function data_remember_user() {
return array(
array( true ),
array( false ),
);
}
public function test_search_users_login() {
$users = get_users(
array(