I was recently tasked with providing the capability for the user to register themselves on a WordPress site with their preferred password. By default, WordPress only requires the user to set up the username and email when registering to the site. Password will be set up by the user later by following the password reset URL provided after the successful registration.

To implement this, a few steps need to be done:

  1. Add the password field to the registration form
  2. Validate the password field upon form submission
  3. Use the provided password for the registered user

Let’s go through them one by one.

Add the password field

This is fairly easy to do. WordPress has a register_form action hook that we can use to add additional HTML content to the registration form. There is a nice password field interaction available when you’re resetting the password, so we will reuse the same functionality on the registration page.

Add these lines into the theme’s functions.php file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

add_action('register_form', function () {
    ?>
        <div class="user-pass1-wrap">
            <p>
                <label for="pass1"><?php _e( 'Password' ); ?></label>
            </p>

            <div class="wp-pwd">
                <input type="password" data-reveal="1" data-pw="<?php echo esc_attr( wp_generate_password( 16 ) ); ?>" name="pass1" id="pass1" class="input password-input" size="24" value="" autocomplete="off" aria-describedby="pass-strength-result" />

                <button type="button" class="button button-secondary wp-hide-pw hide-if-no-js" data-toggle="0" aria-label="<?php esc_attr_e( 'Hide password' ); ?>">
                    <span class="dashicons dashicons-hidden" aria-hidden="true"></span>
                </button>
                <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator' ); ?></div>
            </div>
            <div class="pw-weak">
                <input type="checkbox" name="pw_weak" id="pw-weak" class="pw-checkbox" />
                <label for="pw-weak"><?php _e( 'Confirm use of weak password' ); ?></label>
            </div>
        </div>

        <p class="user-pass2-wrap">
            <label for="pass2"><?php _e( 'Confirm new password' ); ?></label>
            <input type="password" name="pass2" id="pass2" class="input" size="20" value="" autocomplete="off" />
        </p>

        <p class="description indicator-hint"><?php echo wp_get_password_hint(); ?></p>
    <?php
});

The HTML for the password field is taken from the password reset page. You can find the related source code inside the wp-login.php file. The second part of this section is to enable the Javascript interactivity, again, similar to the password reset page. To do that, we’ll need to enqueue the user-profile Javascript to load on the registration page.

1
2
3
4
5
6
7
<?php

add_action('login_enqueue_scripts', function () {
    if (is_on_registration_page() && !wp_script_is('user-profile')) {
        wp_enqueue_script('user-profile');
    }
});

As you can see, I add a special helper function to detect if we are currently on the registration page and check if the script isn’t enqueued yet. If it’s not, then we’ll enqueue it. The helper function is pretty basic and uses the $GLOBALS['pagenow] value for the detection.

1
2
3
4
5
<?php

function is_on_registration_page() {
    return $GLOBALS['pagenow'] == 'wp-login.php' && isset($_REQUEST['action']) && $_REQUEST['action'] == 'register';
}

Now, if we load the registration page, we’ll see the password field is available with the correct interactivity as well as password strength checker.

Validate the password field

By default, the user-profile script will validate the password field on the client-side. If it’s weak, then a checkbox will appear to confirm the use of a weak password. Unless the password field is filled in, the submit button will be disabled. It kind of acts as the first level of validation for us.

In the event of Javascript is disabled, we’ll need to prepare a fallback validation on the server-side to make sure the password is supplied during the form submission. To do so, we can hook into the registration_errors filter to do our custom validation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?php

add_filter('registration_errors', function ($errors) {
    if (empty($_POST['pass1'])) {
        $errors->add('password-required', '<strong>Error</strong>: Please enter a password.');
    }

    if (empty($_POST['pass2'])) {
        $errors->add('password-required', '<strong>Error</strong>: Please enter a password confirmation.');
    }

    return $errors;
});

The first argument passed to the filter is an instance of the WP_Error object, so all we need to do is to implement our validation, then push additional error into the class. What I have in the code snippet above is pretty simple, so feel free to extend this with a more robust validation if needed.

Use the supplied password

If we take a closer look at how WordPress registers a user in wp-includes/user.php, we can see that WordPress will automatically generate a user password.

1
2
3
4
5
6
<?php
// wp-includes/user.php
...

$user_pass = wp_generate_password( 12, false );
$user_id   = wp_create_user( $sanitized_user_login, $user_pass, $user_email );

Luckily, there is a filter that we can hook to inside the wp_generate_password function with the name of random_password. We can use this to our advantage by replacing the generated password with the one supplied by the user during the registration process.

1
2
3
4
5
6
7
8
9
<?php

add_filter('random_password', function ($password) {
    if (is_on_registration_page() && !empty($_POST['pass1'])) {
        $password = $_POST['pass1'];
    }

    return $password;
});

We reuse the helper function again to make sure we only override this during the registration.

Bonus: Disable/Modify the Sent Email

WordPress will send a “Login Details” email to the user, instructing them to reset their password after the account is created. For our case, this doesn’t make sense, so we have two options here:

  • Change the message to a more suitable context
  • Disable that email entirely

We can change the message by hooking into the wp_new_user_notification_email that holds the data that will be passed to the wp_mail function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

add_filter('wp_new_user_notification_email', function ($wp_new_user_notification_email, $user) {
    $message = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
    $message .= __('Password: As entered during your registration') . "\r\n\r\n";
    $message .= wp_login_url() . "\r\n";

    $wp_new_user_notification_email['message'] = $message;

    return $wp_new_user_notification_email;
}, 10, 2);

Instead of asking the user to reset the password, we can mention that the password will be whatever they have set during the registration. If you want to disable this email entirely, simply set the message to an empty string and the email will not be sent out.