Set passwords in WordPress

Set passwords in WordPress

WordPress is the most used Content Management System for websites. However, by default when a new user is registered, a password is sent over e-mail in plain text. Storing of passwords in plain text should always be discouraged. This is why I show how you to make the new user registration email contain a ‘set password’-link, instead plain-text password.

WordPress is the most popular content management system (CMS), powering about half of all websites. WordPress allows for a user and role based access system, which can be used to service members or costumers of a website with password protected access. However, the default password reset functionality sends plain text passwords. Here is an example of what a HTML-formatted password reset e-mail looks like by default in WordPress:

Dummy Logo

Dear customer,

We have received the request to create an account for this e-mail address. Your password has automatically been generated:

llhRGro%Kn6PMV0G

If you did not request the creation of an account you can ignore this message or get in touch with admin@yoursite.com

Storing passwords in plain text should always be discouraged. Even when storing passwords in a “safe” location such as an e-mail inbox (as oppose to post-it’s on your monitor) the presentation of passwords as plain text instills bad habits in your users. This is why it also comes across as totally unprofessional. Thus instead, we would like to achieve something that functions as follows:

Dummy Logo

Dear customer,

We have received the request to create an account for this e-mail address. Please use the button below to set your password:

If you did not request the creation of an account you can ignore this message or get in touch with admin@yoursite.com

The solution is to create a URL like this https://www.yoursite.com/wp-login.php/?action=rp&key=g2V1TVdr9IgFUtPTjEZ0&login=john_doe. You can see that it consists of three parts: 1) A base_url, by default https://www.yoursite.com/wp-login.php, 2) a login identifier, for example john_doe, and crucially 3) a key. This key is a nonce that WordPress stores temporarily and is used to validate the password reset link, for example g2V1TVdr9IgFUtPTjEZ0. This part is not static and must created when the e-mail is sent, so this is the most tricky part.

In this blog post, I preset three solutions for three different plugins, in order of elegance: 1) if you handle your user system using the Ultimate Member plugin, 2) if you are already using the WooCommerce eCommerce plugin, and 3) if you want to be able to send set-password-emails to users manually using the Send User Email-plugin.

Ultimate Member solution

UltimateMember the must downloaded user profile and membership plugin for WordPress. Ultimate Member provides the simplest solution by providing a simple {password_reset_link} placeholder that you can embed in your e-mail template. Go to Ultimate MemberSettingsEmailAccount Welcome Email. There, make sure that the checkbox is checked. You can use the following snippet to send a password reset link in the e-mail: <a href="{password_reset_link}">Reset your password</a> It’s as simple as that!

WooCommerce solution

WooCommerce a free plugin to create an online store on WordPress. WooCommerce does not provide a placeholder for a password reset link. We can write get_password_reset_link ourselves, and inject it straight into the e-mail template. Thus, we first create the get_password_reset_link function to return a set-password url. You should implement this code block as a PHP snippet using any snippet plugin. If you choose to simply add this codeblock to your functions file at ThemesTheme editorfunctions.php (right pane), you risk it being overwritten when your WordPres Theme is updated.

functions.php
Copy
function get_password_reset_link( $user_login = null ) { $base_url = wc_get_endpoint_url( 'lost-password', '', wc_get_page_permalink( 'myaccount' ) ); $userdata = get_user_by('login', $user_login); $user_id = $userdata -> ID; $key = get_password_reset_key( $userdata ); if ( ! is_wp_error( $key ) ) { $base_url .= '?' . http_build_query( array ( 'key' => $key, 'id' => $user_id ) ) ; } // If retrieving the key fails, we still return the url that sends the user // to enter their e-mail and start the regular password reset procedure. return $base_url; }

Second, we embed this function in the e-mail template.

  1. Make the e-mail template editable that is currently sending plain text passwords. Go to WoocommerceSettingsE-mailsNew accountManage. There, press Copy to Theme.
  2. Now edit this template. You can find it at ThemesTheme editorTheme files (right pane) → woocommerceemailscustomer_new_account.php
  3. Here, replace the default plain text user password with a get_password_reset_link call:
customer_new_account.php
Copy
<p> - Your password has been automatically generated: - <strong> - <?php printf(esc_html( $user_pass )) ?> - </strong> + <a href="<?php echo get_password_reset_link( $user_login ); ?>"> + Set your password + </a> </p>

Send Users Email solution

Finally, to send emails to users containing (re)set password links we will use the Send Users Email plugin. Send Users Email provides a way to send email to all system users either by selecting individual users or user roles. Although -like Ultimate Member- this this plugin allows for placeholders such as {{username}} to be embedded in the e-mail it does not provide any password_reset_key functionality out of the box. Additionally, unlike WooCommerce, it does not support direct injection of PHP in its e-mails. To overcome these two challanges we will augment Send Users Email with an additional method in the back end, which we can then invoke just like {{username}}, in the front end.

NB: Editing plugin source code directly has its disadvantages. Frist, make sure you have a backup of your current WordPress state, and test if you know how to revert changes before committing to them. If you are hosting your WordPress site on AWS LightSail like me, it is best to spin up a copy of your current instance and use it as a development server. Then only when you are done testing, you connect the static IP-adress that is connected to your old LightSail instance to the new and improved instance.

Second, changes made may not carry to updated updated versions of the Send Users Email plugin. So make sure you are using some kind of version control that can merge your changes to the updated plugin any time you perform updates.

For now, I assume you have no version control in place, so let us create a new location with a copy that we can always revert to. 1) Create a folder to store our edited file, safe from any updates. 2) Go to the location of the original file. 3) Copy the original file to our newly created location. 4) Copy a backup of the original file to our newly created location. 5) Go to our newly created location and 6) start making changes using your editor of choice:

bitnami@ip-127-26-2-172:~$ sudo mkdir -p /bitnami/wordpress/wp-content/plugins_changes/send-users-email/admin/ cd /bitnami/wordpress/wp-content/plugins/send-users-email/admin sudo cp class-send-users-email-admin.php /bitnami/wordpress/wp-content/plugins_changes/send-users-email/admin/class-send-users-email-admin.php sudo cp class-send-users-email-admin.php /bitnami/wordpress/wp-content/plugins_changes/send-users-email/admin/.class-send-users-email-admin.php cd /bitnami/wordpress/wp-content/plugins_changes/send-users-email/admin/ vim class-send-users-email-admin.php

We will insert four lines at two locations. Line numbers provided for reference are for Send Users Email version1.5.1, as of February 2024. The first two lines we will insert will retrieve the key information for the user:

class-send-users-email-admin.php
Copy
327 // Email header setup 328 $headers = $this->get_email_headers(); 329 foreach ( $user_details as $user ) { 330 $email_body = $message; 331 $username = $user->user_login; 332 $display_name = $user->display_name; 333 $user_email = sanitize_email( $user->user_email ); 334 $user_id = (int) $user->ID; 335 $user_meta = get_user_meta( $user->ID ); 336 $first_name = $user_meta['first_name'][0] ?? ''; 337 $last_name = $user_meta['last_name'][0] ?? ''; 338 $wp_user = new WP_User( $user_id ); +++ $pw_reset_key = get_password_reset_key( $wp_user ); 339 // Replace placeholder with user content 340 $email_body = $this->replace_placeholder( 341 $email_body, 342 $username, 343 $display_name, 344 $first_name, 345 $last_name, 346 $user_email, 347 $user_id, +++ $pw_reset_key 348 ); 349 $email_subject = stripslashes_deep( $subject );

Next, insert the following two lines into the replace_placeholder function. These will perform the actual replacement:

class-send-users-email-admin.php
Copy
725 /** 726 * Replace placeholder text to content 727 */ 728 private function replace_placeholder( 729 $email_body, 730 $username, 731 $display_name, 732 $first_name, 733 $last_name, 734 $user_email, 735 $user_id, +++ $pw_reset_key 736 ) { 737 $email_body = str_replace( '{{username}}', $username, $email_body ); 738 $email_body = str_replace( '{{user_display_name}}', $display_name, $email_body ); 739 $email_body = str_replace( '{{user_first_name}}', $first_name, $email_body ); 740 $email_body = str_replace( '{{user_last_name}}', $last_name, $email_body ); 741 $email_body = str_replace( '{{user_email}}', $user_email, $email_body ); 742 $email_body = str_replace( '{{user_id}}', $user_id, $email_body ); +++ $email_body = str_replace( '{{password_reset_key}}', $pw_reset_key, $email_body ); 743 744 return wpautop( $email_body ); 745 }

Additionally, you can update the instructions at /bitnami/wordpress/wp-content/plugins/send-users-email/admin/partials/templates/placeholder-instruction.php to include this new functionality by adding this row:

placeholder-instruction.php
Copy
42 </tr> ++ <tr> ++ <td> ++ {{password_reset_key}}<br> ++ this placeholder to generate and display a password reset key. ++ </td> ++ </tr> 43 </table> 44 <div class="sue-messages"></div> 45 </div> 46 </div>

Now, you can easily construct a password reset button URL in e-mails that you send with Send User Email:

Copy
<a href="https://www.yoursite.com/wp-login.php/?action=rp&key={{password_reset_key}}&login={{username}}">Set your password</a>