43

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. If I manually visit domain.example/forgot-password?user_login=username it does send the reset pass email fine.

3
  • by app do you mean plugin
    – Amit Kosti
    Commented Aug 1, 2012 at 12:19
  • no, app - android
    – Nimbuz
    Commented Aug 1, 2012 at 12:51
  • doesn't work what happens?
    – pcarvalho
    Commented Aug 3, 2012 at 8:06

9 Answers 9

28
+100

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:') . "\r\n\r\n";
    $message .= network_home_url( '/' ) . "\r\n\r\n";
    $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
    $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";

    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";
}
1
  • This isn't working for me exactly right. I've entered it into a custom plugin and put it on my site but it's causing an issue where when I install the plugin and then click 'Logout' it won't log me out of the site. Any ideas why? Commented Oct 11, 2013 at 8:04
21

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:') . "\r\n\r\n";
    $message .= network_home_url( '/' ) . "\r\n\r\n";
    $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
    $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";

    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>';;
}
2
  • This worked out for me. The accepted answer for 3.8.1 did not worked and ended in expired key. Specifically, in $key processing and updating user_activation_key.
    – Rolice
    Commented Mar 27, 2014 at 21:27
  • 1
    This answer also resulted in an expired link for me and did not work.
    – carbide20
    Commented Jan 5, 2017 at 16:57
12

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:') . "\r\n\r\n";
            $message .= network_home_url( '/' ) . "\r\n\r\n";
            $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
            $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
            $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
            $message .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
0
8

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

0
4

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:') . "\r\n\r\n";
    $message .= network_home_url( '/' ) . "\r\n\r\n";
    $message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
    $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
    $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
    $message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";

    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>';;
}
4

Combined Example ( Tested in 5.2 )

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>

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;
})

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:') . "\r\n\r\n";
        $message  = "<p>Hi ".ucfirst( $user_info->first_name ).",</p>";
        $message .= network_home_url( '/' ) . "\r\n\r\n";
        $message .= sprintf(__('Username: %s'), $user_info->user_login) . "\r\n\r\n";
        $message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
        $message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
        $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() ]
    ]);
}

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;
  }
}

1
  • And the form for creating a new password after reset?)
    – aprinciple
    Commented Jun 4, 2022 at 16:50
1

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!!!

1

I couldn't get any of these to work elegantly in WP (testing in WP 6.2.2) so after looking at the WP repository on Github it actually looks surprisingly simple in the AJAX request for resetting a password.

/* Assuming you already have $user_login variable set */

$results = retrieve_password( $user_login ); //Trigger password reset email
if($results === true) {
    // Success
} else {
    // Error
}

If you've only got the user ID you can retrieve the login using the following code:

/* Assuming you already have $user_id variable set */

$user = get_userdata( $user_id);
$results = retrieve_password( $user->user_login ); // Trigger password reset email
0

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)

Not the answer you're looking for? Browse other questions tagged or ask your own question.