check_password_reset_key

The timeline below displays how wordpress function check_password_reset_key has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.4

/**
 * Retrieves a user row based on password reset key and login.
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $user = get_user_by('login', $login);
    if (!$user) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (str_contains($user->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $user->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $user->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return $user;
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed.
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($user->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $user->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 6.3

/**
 * Retrieves a user row based on password reset key and login.
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $user = get_user_by('login', $login);
    if (!$user) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (str_contains($user->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $user->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $user->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return $user;
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed.
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($user->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $user->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 6.1

/**
 * Retrieves a user row based on password reset key and login.
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $user = get_user_by('login', $login);
    if (!$user) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($user->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $user->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $user->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return $user;
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed.
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($user->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $user->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 5.4

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $user = get_user_by('login', $login);
    if (!$user) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($user->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $user->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $user->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return $user;
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed.
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($user->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $user->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 5.3

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $user = get_user_by('login', $login);
    if (!$user) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($user->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $user->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $user->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return $user;
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($user->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $user->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 5.2

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key.'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key.'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key.'));
        $user_id = $row->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key.'));
}

WordPress Version: 4.8

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.7

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.6

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filters the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filters the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.5

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filter the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    if (!$pass_key) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.3

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @since 3.1.0
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    /**
     * Filter the expiration time of password reset keys.
     *
     * @since 4.3.0
     *
     * @param int $expiration The expiration time in seconds.
     */
    $expiration_duration = apply_filters('password_reset_expiration', DAY_IN_SECONDS);
    if (false !== strpos($row->user_activation_key, ':')) {
        list($pass_request_time, $pass_key) = explode(':', $row->user_activation_key, 2);
        $expiration_time = $pass_request_time + $expiration_duration;
    } else {
        $pass_key = $row->user_activation_key;
        $expiration_time = false;
    }
    $hash_is_correct = $wp_hasher->CheckPassword($key, $pass_key);
    if ($hash_is_correct && $expiration_time && time() < $expiration_time) {
        return get_userdata($row->ID);
    } elseif ($hash_is_correct && $expiration_time) {
        // Key has an expiration time that's passed
        return new WP_Error('expired_key', __('Invalid key'));
    }
    if (hash_equals($row->user_activation_key, $key) || $hash_is_correct && !$expiration_time) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used.
         *
         * @since 3.7.0 Previously plain-text keys were stored in the database.
         * @since 4.3.0 Previously key hashes were stored without an expiration time.
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.2

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @global wpdb         $wpdb      WordPress database object for queries.
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    if ($wp_hasher->CheckPassword($key, $row->user_activation_key)) {
        return get_userdata($row->ID);
    }
    if ($key === $row->user_activation_key) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used (plain-text key was stored in the database).
         *
         * @since 3.7.0
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 4.0

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @global wpdb $wpdb WordPress database object for queries.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . WPINC . '/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    if ($wp_hasher->CheckPassword($key, $row->user_activation_key)) {
        return get_userdata($row->ID);
    }
    if ($key === $row->user_activation_key) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used (plain-text key was stored in the database).
         *
         * @since 3.7.0
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 3.9

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @global wpdb $wpdb WordPress database object for queries.
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . 'wp-includes/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    if ($wp_hasher->CheckPassword($key, $row->user_activation_key)) {
        return get_userdata($row->ID);
    }
    if ($key === $row->user_activation_key) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used (plain-text key was stored in the database).
         *
         * @since 3.7.0
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}

WordPress Version: 3.7

/**
 * Retrieves a user row based on password reset key and login
 *
 * A key is considered 'expired' if it exactly matches the value of the
 * user_activation_key field, rather than being matched after going through the
 * hashing process. This field is now hashed; old values are no longer accepted
 * but have a different WP_Error code so good user feedback can be provided.
 *
 * @uses $wpdb WordPress Database object
 *
 * @param string $key       Hash to validate sending user's password.
 * @param string $login     The user login.
 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
 */
function check_password_reset_key($key, $login)
{
    global $wpdb, $wp_hasher;
    $key = preg_replace('/[^a-z0-9]/i', '', $key);
    if (empty($key) || !is_string($key)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($login) || !is_string($login)) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    $row = $wpdb->get_row($wpdb->prepare("SELECT ID, user_activation_key FROM {$wpdb->users} WHERE user_login = %s", $login));
    if (!$row) {
        return new WP_Error('invalid_key', __('Invalid key'));
    }
    if (empty($wp_hasher)) {
        require_once ABSPATH . 'wp-includes/class-phpass.php';
        $wp_hasher = new PasswordHash(8, true);
    }
    if ($wp_hasher->CheckPassword($key, $row->user_activation_key)) {
        return get_userdata($row->ID);
    }
    if ($key === $row->user_activation_key) {
        $return = new WP_Error('expired_key', __('Invalid key'));
        $user_id = $row->ID;
        /**
         * Filter the return value of check_password_reset_key() when an
         * old-style key is used (plain-text key was stored in the database).
         *
         * @since 3.7.0
         *
         * @param WP_Error $return  A WP_Error object denoting an expired key.
         *                          Return a WP_User object to validate the key.
         * @param int      $user_id The matched user ID.
         */
        return apply_filters('password_reset_key_expired', $return, $user_id);
    }
    return new WP_Error('invalid_key', __('Invalid key'));
}