简体   繁体   中英

errors=invalidkey for a WordPress Custom Password recovery page

WordPress v5.4.1, I have the below code to send the password reset mail from custom page.

Followed the https://code.tutsplus.com/tutorials/build-a-custom-wordpress-user-flow-part-3-password-reset--cms-23811 .

// Custom message
function replace_retrieve_password_message($message, $key, $user_login, $user_data) {

    global $wpdb;

    $key = $wpdb->get_var("SELECT user_activation_key FROM $wpdb->users WHERE user_login ='" . $user_login . "'");
    if (empty($key)) {
        //generate reset key
        $key = wp_generate_password(20, false);
        $wpdb->update($wpdb->users, array('user_activation_key' => $key), array('user_login' => $user_login));
    }
    // Create new message
    $msg = __('Hello!') . "\r\n\r\n";
    $msg .= sprintf(__('You asked us to reset your password for your account using the email address %s.'), $user_login) . "\r\n\r\n";
    $msg .= __("If this was a mistake, or you didn't ask for a password reset, just ignore this email and nothing will happen.") . "\r\n\r\n";
    $msg .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $msg .= site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . "\r\n\r\n";
    $msg .= __('Thanks!') . "\r\n";

    return $msg;
}

add_filter('retrieve_password_message', 'replace_retrieve_password_message', 10, 4);

User is getting the password reset email and while clicking on the link, it gives errors=invalidkey .
I have tried removing the custom $key function to have WordPress default key generation. Then I will have password reset page, but any action is showing errors=invalidkey .

Why are you regenerating the $key ? Why not just use the one passed to your callback?

But if you must regenerate it, then you could just use get_password_reset_key() :

$key = get_password_reset_key( $user_data );

The reason for your invalidkey error is that you are just creating the random password and using it as is. For it to be valid reset key it needs to be hashed.

Reference: https://core.trac.wordpress.org/browser/tags/5.4/src/wp-includes/user.php#L2316

So if you want to use your approach you just need to hash your key like this,

// Custom message
function replace_retrieve_password_message($message, $key, $user_login, $user_data) {

    global $wpdb;
    global $wp_hasher;

    $key = $wpdb->get_var("SELECT user_activation_key FROM $wpdb->users WHERE user_login ='" . $user_login . "'");
    if (empty($key)) {
        //generate reset key
        $key = wp_generate_password(20, false);

        if ( empty( $wp_hasher ) ) {
            require_once ABSPATH . WPINC . '/class-phpass.php';
            $wp_hasher = new PasswordHash( 8, true );
        }

        $key = time() . ':' . $wp_hasher->HashPassword( $key );

        $wpdb->update($wpdb->users, array('user_activation_key' => $key), array('user_login' => $user_login));
    }
    // Create new message
    $msg = __('Hello!') . "\r\n\r\n";
    $msg .= sprintf(__('You asked us to reset your password for your account using the email address %s.'), $user_login) . "\r\n\r\n";
    $msg .= __("If this was a mistake, or you didn't ask for a password reset, just ignore this email and nothing will happen.") . "\r\n\r\n";
    $msg .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $msg .= site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . "\r\n\r\n";
    $msg .= __('Thanks!') . "\r\n";

    return $msg;
}

add_filter('retrieve_password_message', 'replace_retrieve_password_message', 10, 4);

But I have written this just for your understanding of the issue. I do not recommend it.

Recommended Approach:

As the other answer also suggests the right way to do this is to just skip all the key creation that your doing manually and use the right function for this ie get_password_reset_key() , so your code with this approach would look like,

// Custom message
function replace_retrieve_password_message($message, $key, $user_login, $user_data) {

    $key = get_password_reset_key( $user_data );

    // Create new message
    $msg = __('Hello!') . "\r\n\r\n";
    $msg .= sprintf(__('You asked us to reset your password for your account using the email address %s.'), $user_login) . "\r\n\r\n";
    $msg .= __("If this was a mistake, or you didn't ask for a password reset, just ignore this email and nothing will happen.") . "\r\n\r\n";
    $msg .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $msg .= site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . "\r\n\r\n";
    $msg .= __('Thanks!') . "\r\n";

    return $msg;
}

add_filter('retrieve_password_message', 'replace_retrieve_password_message', 10, 4);

As you can see that it is more cleaner. It is also better to use the existing functions where available and applicable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM