Sending the reset password link programatically

The question:

I have this manually created page:

$user_login = sanitize_text_field( $_GET['user_login'] );

if ( username_exists( $user_login ) ||  email_exists($user_login) ) { ?>
<!--Everything has been validated, proceed ....-->

<!DOCTYPE HTML>
<html lang="en-US">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">
        function submit()
        {
            var f = document.getElementById('lostpasswordform');
            f.onclick = function () { };
            document.lostpasswordform.submit();
        }
    </script>
</head>
<body onload="submit()">

    <form name="lostpasswordform" id="lostpasswordform" action="<?php echo esc_url( site_url( 'wp-login.php?action=lostpassword', 'login_post' ) ); ?>" method="post">

        <input type="hidden" name="user_login" id="user_login" class="input" value="<?php echo ($user_login); ?>" />

    <?php do_action('lost_password'); ?>

    </form>

</body>
</html>

<?php
    echo "SUCCESS";
    exit();
} else {
    echo "Entered Username or Email was incorrect, please try again!";
}

… everything seems right but it doesn’t work when called from an app, but if I manually visit domain.com/forgot-password?user_login=username it does send the reset pass email fine.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

So if you want to send the reset password link and you have access to the code base, you can use the following snippet and you can modify it further. Actually this code is a slightly modified version of wp-login.php

/**
 * Handles sending password retrieval email to user.
 *
 * @uses $wpdb WordPress Database object
 * @param string $user_login User Login or Email
 * @return bool true on success false on error
 */
function retrieve_password($user_login) {
    global $wpdb, $current_site;

    if ( empty( $user_login) ) {
        return false;
    } else if ( strpos( $user_login, '@' ) ) {
        $user_data = get_user_by( 'email', trim( $user_login ) );
        if ( empty( $user_data ) )
           return false;
    } else {
        $login = trim($user_login);
        $user_data = get_user_by('login', $login);
    }

    do_action('lostpassword_post');


    if ( !$user_data ) return false;

    // redefining user_login ensures we return the right case in the email
    $user_login = $user_data->user_login;
    $user_email = $user_data->user_email;

    do_action('retreive_password', $user_login);  // Misspelled and deprecated
    do_action('retrieve_password', $user_login);

    $allow = apply_filters('allow_password_reset', true, $user_data->ID);

    if ( ! $allow )
        return false;
    else if ( is_wp_error($allow) )
        return false;

    $key = $wpdb->get_var($wpdb->prepare("SELECT user_activation_key FROM $wpdb->users WHERE user_login = %s", $user_login));
    if ( empty($key) ) {
        // Generate something random for a key...
        $key = wp_generate_password(20, false);
        do_action('retrieve_password_key', $user_login, $key);
        // Now insert the new md5 key into the db
        $wpdb->update($wpdb->users, array('user_activation_key' => $key), array('user_login' => $user_login));
    }
    $message = __('Someone requested that the password be reset for the following account:') . "rnrn";
    $message .= network_home_url( '/' ) . "rnrn";
    $message .= sprintf(__('Username: %s'), $user_login) . "rnrn";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "rnrn";
    $message .= __('To reset your password, visit the following address:') . "rnrn";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">rn";

    if ( is_multisite() )
        $blogname = $GLOBALS['current_site']->site_name;
    else
        // The blogname option is escaped with esc_html on the way into the database in sanitize_option
        // we want to reverse this for the plain text arena of emails.
        $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);

    $title = sprintf( __('[%s] Password Reset'), $blogname );

    $title = apply_filters('retrieve_password_title', $title);
    $message = apply_filters('retrieve_password_message', $message, $key);

    if ( $message && !wp_mail($user_email, $title, $message) )
        wp_die( __('The e-mail could not be sent.') . "<br />n" . __('Possible reason: your host may have disabled the mail() function...') );

    return true;
}

$user_login = sanitize_text_field( $_GET['user_login'] );

if (retrieve_password($user_login)) {
    echo "SUCCESS";
} else {
    echo "ERROR";
}

Method 2

Previous answer didn’t worked for me (says that code is invalid, on wp login page), probably because answer is 1,5 yr old, and something is changed in WP code, so I have updated this code a bit (also from wp-login.php), here it is:

function retrieve_password($user_login){
    global $wpdb, $wp_hasher;

    $user_login = sanitize_text_field($user_login);
    
    if ( empty( $user_login) ) {
        return false;
    } else if ( strpos( $user_login, '@' ) ) {
        $user_data = get_user_by( 'email', trim( $user_login ) );
        if ( empty( $user_data ) )
           return false;
    } else {
        $login = trim($user_login);
        $user_data = get_user_by('login', $login);
    }

    do_action('lostpassword_post');


    if ( !$user_data ) return false;

    // redefining user_login ensures we return the right case in the email
    $user_login = $user_data->user_login;
    $user_email = $user_data->user_email;

    do_action('retreive_password', $user_login);  // Misspelled and deprecated
    do_action('retrieve_password', $user_login);

    $allow = apply_filters('allow_password_reset', true, $user_data->ID);

    if ( ! $allow )
        return false;
    else if ( is_wp_error($allow) )
        return false;
        
    $key = wp_generate_password( 20, false );
    do_action( 'retrieve_password_key', $user_login, $key );

    if ( empty( $wp_hasher ) ) {
        require_once ABSPATH . 'wp-includes/class-phpass.php';
        $wp_hasher = new PasswordHash( 8, true );
    }
    $hashed = time() . ':' . $wp_hasher->HashPassword( $key );
    $wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user_login ) );

    $message = __('Someone requested that the password be reset for the following account:') . "rnrn";
    $message .= network_home_url( '/' ) . "rnrn";
    $message .= sprintf(__('Username: %s'), $user_login) . "rnrn";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "rnrn";
    $message .= __('To reset your password, visit the following address:') . "rnrn";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">rn";

    if ( is_multisite() )
        $blogname = $GLOBALS['current_site']->site_name;
    else
        $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);

    $title = sprintf( __('[%s] Password Reset'), $blogname );

    $title = apply_filters('retrieve_password_title', $title);
    $message = apply_filters('retrieve_password_message', $message, $key);

    if ( $message && !wp_mail($user_email, $title, $message) )
        wp_die( __('The e-mail could not be sent.') . "<br />n" . __('Possible reason: your host may have disabled the mail() function...') );

    echo '<p>Link for password reset has been emailed to you. Please check your email.</p>';;
}

Method 3

None of the above answer worked for me so I looked into wp-login.php for their default reset functionality. They used get_password_reset_key( $userData ) function . In case someone stuck up on above answers here’s my solution :-

            $userData = get_userdata($user_id);             

            $user_login = $userData->user_login;
            $user_email = $userData->user_email;
            $key = get_password_reset_key( $userData );


            $message = __('Someone requested that the password be reset for the following account:') . "rnrn";
            $message .= network_home_url( '/' ) . "rnrn";
            $message .= sprintf(__('Username: %s'), $user_login) . "rnrn";
            $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "rnrn";
            $message .= __('To reset your password, visit the following address:') . "rnrn";
            $message .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');

Method 4

I noticed that after upgrading WordPress to Version 4.3 that the above no longer worked for my custom plugin. It would always report that the key was invalid.

Change:

$hashed = $wp_hasher->HashPassword( $key );

to

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

This fixed the issue for me, hope it helps someone else

Method 5

WordPress 4.3.1

function retrieve_password($user_login){
    global $wpdb, $wp_hasher;
    $user_login = sanitize_text_field($user_login);
    if ( empty( $user_login) ) {
        return false;
    } else if ( strpos( $user_login, '@' ) ) {
        $user_data = get_user_by( 'email', trim( $user_login ) );
        if ( empty( $user_data ) )
           return false;
    } else {
        $login = trim($user_login);
        $user_data = get_user_by('login', $login);
    }

    do_action('lostpassword_post');
    if ( !$user_data ) return false;
    // redefining user_login ensures we return the right case in the email
    $user_login = $user_data->user_login;
    $user_email = $user_data->user_email;
    do_action('retreive_password', $user_login);  // Misspelled and deprecated
    do_action('retrieve_password', $user_login);
    $allow = apply_filters('allow_password_reset', true, $user_data->ID);
    if ( ! $allow )
        return false;
    else if ( is_wp_error($allow) )
        return false;
    $key = wp_generate_password( 20, false );
    do_action( 'retrieve_password_key', $user_login, $key );

    if ( empty( $wp_hasher ) ) {
        require_once ABSPATH . 'wp-includes/class-phpass.php';
        $wp_hasher = new PasswordHash( 8, true );
    }
    $hashed = $wp_hasher->HashPassword( $key );    
    $wpdb->update( $wpdb->users, array( 'user_activation_key' => time().":".$hashed ), array( 'user_login' => $user_login ) );
    $message = __('Someone requested that the password be reset for the following account:') . "rnrn";
    $message .= network_home_url( '/' ) . "rnrn";
    $message .= sprintf(__('Username: %s'), $user_login) . "rnrn";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "rnrn";
    $message .= __('To reset your password, visit the following address:') . "rnrn";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">rn";

    if ( is_multisite() )
        $blogname = $GLOBALS['current_site']->site_name;
    else
        $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);

    $title = sprintf( __('[%s] Password Reset'), $blogname );

    $title = apply_filters('retrieve_password_title', $title);
    $message = apply_filters('retrieve_password_message', $message, $key);

    if ( $message && !wp_mail($user_email, $title, $message) )
        wp_die( __('The e-mail could not be sent.') . "<br />n" . __('Possible reason: your host may have disabled the mail() function...') );

    echo '<p>Link for password reset has been emailed to you. Please check your email.</p>';;
}

Method 6

Combined Example ( Tested in 5.2 )

Step 1:

  1. Frontend Form
<form id="form-password-reset" action="<?php echo wp_lostpassword_url(); ?>" method="post">
  <?php wp_nonce_field( 'ajax-resetpassword-nonce', 'security' ); ?>
  <div class="text-align-center margin-bottom-32">
     <h3 class="h3">Reset Your Password</h3>
  </div>
  <div class="row">
     <div class="col-12 col-md-4">
        <div class="form-group">
           <label for="auth-password-reset-email">Email</label>
           <div class="form-input-wrapper">
              <input type="email" class="form-input" id="auth-password-reset-email" name="email" placeholder="eg. [email protected]">
           </div>
        </div>
        <button type="submit" class="btn-solid btn-primary btn-block">
        Submit</button>
        <p class="form__error margin-top-16">
           // TODO you can show the message here
        </p>
     </div>
     <div class="col-12 col-md-4"></div>
  </div>
</form>
  1. Js to handle the ajax submit

        $(form).on('submit', function (event) {
        event.preventDefault(event)
        const email = $(`#auth-password-reset-email`).val()
        const security = $(`#security`).val()
        const action = 'authResetPassword'
        var has_error = false;
    
        if (email.length <= 0) {
         // TODO
          has_error = true;
        }
    
        if (security.length <= 0) {
          // TODO
          has_error = true;
        }
    
        if (has_error == true) {
          // TODO
          return false
        }
    
        $.ajax({
          type: 'POST',
          dataType: 'json',
          url: ajax.ajaxurl,
          data: {  action, email, security },
          success: function (data) {
            if (data.status) {
               // TODO
            }
            if (!data.status) {
                // TODO
            }
          }
        })
    
        return false;
      })
    
    
  2. Server side to generate the reset link and trigger the e-mail


try {
            check_ajax_referer('ajax-resetpassword-nonce', 'security');
            $response = [
                "status" => false,
                "error" => true,
                "data" => "",
            ];

            extract($_POST);

            $user_email = sanitize_text_field($email);
           
            $user = get_user_by( 'email', $user_email );
            if( $user instanceof WP_User ) {
                $user_id = $user->ID;
                $user_info = get_userdata($user_id);
                $unique = get_password_reset_key( $user_info );
                $unique_url = network_site_url("wp-login.php?action=rp&key=$unique&login=" . rawurlencode($user_info->user_login), 'login');
              
                $subject  = "Reset Password Link";
                $message = __('Someone requested that the password be reset for the following account:') . "rnrn";
                $message  = "<p>Hi ".ucfirst( $user_info->first_name ).",</p>";
                $message .= network_home_url( '/' ) . "rnrn";
                $message .= sprintf(__('Username: %s'), $user_info->user_login) . "rnrn";
                $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "rnrn";
                $message .= __('To reset your password, visit the following address:') . "rnrn";
                $message .=  $unique_url;

                wp_mail( $user_email, $subject, $message ); // TODO
                $response['data'] = [ "message" =>  __('Password reset link sent.'), 'reset_link' => $unique_url ];
                wp_send_json($response);
            }

            $response['data'] = [ "message" =>  __('Email address not exists.') ];
            wp_send_json($response);


        } catch (Exception $e) {
            wp_send_json([
                "status" => false,
                "error" => false,
                "data" => ["message" => $e->getMessage() ]
            ]);
        }

Note: To show your own custom reset password form

add_action( 'login_form_lostpassword', 'redirect_to_custom_lostpassword' );
function redirect_to_custom_lostpassword() {
  if ( 'GET' == $_SERVER['REQUEST_METHOD'] ) {
      if ( is_user_logged_in() ) {
         // TODO
          exit;
      }

      wp_redirect( home_url( 'custom-password-lost' ) ); // TODO
      exit;
  }
}

Method 7

Since the above solution was not working for me I did a minor changes in the bhavesh vala’s code.

Replace :

$key = wp_generate_password( 20, false );

With this :

$key = get_password_reset_key( $user_data );

And you don’t need to use the update query, so remove the below query :

$wpdb->update( $wpdb->users, array( 'user_activation_key' => time().":".$hashed ), array( 'user_login' => $user_login ) );

Happy coding!!!

Method 8

Try this

$wpdb->update( $wpdb->users, array( 'user_activation_key' => $key ), array( 'user_login' => $user_login ) ); 

instead of

$wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user_login ) );

It worked me (wordpress 4.3.1)


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment