Lock a WordPress user account to prevent login

Login Denied WordPress user account locked

This copy-and-paste tutorial adds a “lock user account” function to your WordPress site. Block individual users from logging-in to your site and terminate any active sessions they might have. This is useful if you’ve got historic administrators you don’t want to delete, but you need to prevent them gaining access to the site in the future.

For locked-out users…

  • Terminate the user’s active login sessions (if they have any)
  • Hook the WordPress authenticate filter to prevent them from logging in
  • In the back-end WP Users area, add a column so we can see which users are locked-out

Scaffold the code

All this code is implemented in the back-end using PHP, so start by creating a new file in your custom child theme called wpt-lock-user-accounts.php and paste the following into it:

<?php

/**
 * WP Tutorials :: Lock User Account (WPTLUA)
 *
 * https://wp-tutorials.tech/refine-wordpress/lock-a-wordpress-user-account-to-prevent-login/
 *
 */

defined('WPINC') || die();

const META_IS_ACCOUNT_LOCKED = '_is_account_locked';

/**
 * Given a valid user_id, lock the user account and delete all currently
 * logged-in sessions for that user.
 */
function wptlua_lock_user_account(int $user_id)
{
    if ($user_id <= 0) {
        error_log('WPTLUA: Lock : Invalid user id ' . $user_id);
    } else {
        update_user_meta($user_id, META_IS_ACCOUNT_LOCKED, 'yes');

        if (!empty(($sessions = WP_Session_Tokens::get_instance($user_id)))) {
            $sessions->destroy_all();
        }
    }
}

/**
 * Unlock a user's account by their ID.
 */
function wptlua_unlock_user_account(int $user_id)
{
    if ($user_id <= 0) {
        error_log('WPTLUA: Unlock : Invalid user id ' . $user_id);
    } else {
        delete_user_meta($user_id, META_IS_ACCOUNT_LOCKED);
    }
}

/**
 * Is a user's account locked?
 */
function wptlua_is_user_account_locked(int $user_id): bool
{
    $is_locked = false;
    if ($user_id > 0) {
        $is_locked = (bool) filter_var(get_user_meta($user_id, META_IS_ACCOUNT_LOCKED, true), FILTER_VALIDATE_BOOLEAN);
    }

    return $is_locked;
}

These are our core functions that do the actual work of locking & unlocking user accounts, by setting a user meta field called “_is_account_locked”.

Next, open your child theme’s “functions.php” file and add the following couple of lines:

// Headwall WP Tutorials : Lock user accounts and log them out of existing sessions.
require_once dirname(__FILE__) . '/wpt-lock-user-accounts.php';

Save everything and reload your site’s admin dashboard to make sure nothing is broken.

Note: If you prefer to use a plugin for managing code snippets (instead of a PHP file in your child theme) you can put all the code for this project into a single managed snippet.

Add the lock/unlock checkbox

Adding the checkbox to the user profile editor page needs two functions:

  1. Inject an HTML snippet to render the checkbox input and label.
  2. Hook the save/update user action, detect if the option is checked or not and lock/unlock the account accordingly.

Open “wpt-lock-user-accounts.php” and add this to the end of the file:

/**
 * Add our input checkbox to the Edit User Profile page.
 */
function wptlua_extra_user_profile_fields($user)
{
    if (empty($user)) {
        // No user specified? Weird.
    } elseif (!current_user_can('administrator')) {
        // Only administrators can lock/unlock users.
    } else {
        echo '<table class="form-table">';
        echo '<tbody>';
        echo '<tr>';

        printf('<th>%s</th>', esc_html__('Lock account', 'wp-tutorials'));

        echo '<td>';

        // If the user account is already locked, make sure our checkbox is checked.
        $props = '';
        if (wptlua_is_user_account_locked($user->ID)) {
            $props = 'checked="checked"';
        }
        printf('<input type="checkbox" name="%s" id="UserAccountLock" %s />', META_IS_ACCOUNT_LOCKED, $props);
        printf('<label for="UserAccountLock">%s</label>', esc_html__('Prevent the user from logging in to the site', 'wp-tutorials'));

        echo '</td>';

        echo '</tr>';
        echo '</tbody>';
        echo '</table>'; // .form-table
    }
}
add_action('edit_user_profile', 'wptlua_extra_user_profile_fields', 10, 1);

/**
 * When the user profile page is saved, check to see if we need to
 * lock/unlock this account.
 */
function wptlua_update_user_profile(int $user_id)
{
    if (!current_user_can('administrator')) {
        // Only administrators can lock/unlock users.
    } elseif (!array_key_exists(META_IS_ACCOUNT_LOCKED, $_POST)) {
        // The input is not checked, so unlock the user account.
        wptlua_unlock_user_account($user_id);
    } else {
        // The input is checked, so lock the user account now.
        wptlua_lock_user_account($user_id);
    }
}
add_action('edit_user_profile_update', 'wptlua_update_user_profile', 10, 1);

Notice how each function does a check to make sure that the current logged-in user is an administrator before we render or process our checkbox.

Edit a user profile (not your own) and verify the new HTML is in place. Tick the “new”Lock account” option, save the user and make sure option is still ticked when the page reloads. If that works, we know that both of the new functions are working correctly.

Lock a WordPress user account
Lock a WordPress user account

Add a custom column to the admin users table

To add a custom column to the admin users table, we need two more functions:

  1. Define the custom column and give it a title. This function is called once, before the table is rendered.
  2. Populate the column for each row/user.

In “wpt-lock-user-accounts.php”, add the following code to the end of the file:

/**
 * Add a column to the Users table to show if the account is locked.
 */
function wptlua_manage_users_columns($columns)
{
    $columns[META_IS_ACCOUNT_LOCKED] = '<span class="dashicons dashicons-lock"></span>';

    return $columns;
}
add_filter('manage_users_columns', 'wptlua_manage_users_columns');

/**
 * Render our custom column for each user record in the table.
 */
function wptlua_manage_users_custom_column($output, $column_name, $user_id)
{
    if ($column_name !== META_IS_ACCOUNT_LOCKED) {
        // This is not our column - do nothing.
    } elseif (!wptlua_is_user_account_locked($user_id)) {
        // The user account is not locked so we can leave the cell empty.
    } else {
        $output = '<span class="dashicons dashicons-lock" style="color: red"></span>';
    }

    return $output;
}
add_filter('manage_users_custom_column', 'wptlua_manage_users_custom_column', 10, 3);

Save the changes and go to your admin users table – you should now see something like this:

Admin users table with a column showing locked-out users
Admin users table with a column showing locked-out users

Prevent locked-out users from logging in

Our final chunk of code will hook the WordPress authentication sequence and return an error if a locked user tries to log in. Add this last snippet of PHP to your project:

/**
 * Prevent locked user accounts from logging in.
 */
function wptlua_check_attempted_login($user, $username, $password)
{
    if (!is_a($user, 'WP_User')) {
        // We can only check the account status if the user has been authenticated.
    } elseif (!wptlua_is_user_account_locked($user->ID)) {
        // The user account is not locked. Proceed as normal.
    } else {
        // Replace the authenticated user with a WP_Error object.
        $user = new WP_Error(
            'account_locked', // ...
            'Your account has been locked. Please contact the site administrator for assistance.'
        );
    }

    return $user;
}
add_filter('authenticate', 'wptlua_check_attempted_login', 30, 3);

Although there’s not much code in here, we need to be very careful. Especially as this is called as part of the login procedure – we don’t want to accidentally break our site’s security. The documentation for the “authenticate” hook says that the $user parameter might be null|WP_User|WP_Error, so we need to be sure it’s a WP_User object before we try to access the ID property. We do this with the PHP function is_a() at the start of the function.

If we think that the user account is locked, all we do is return a WP_Error object instead of an authenticated WP_User object.

Run a final test by opening a private browsing window and trying to log in with a locked user account.

You should see your error message above the login box.

If you’re running WooCommerce, this should also work on the front-end login form, so make sure to test that too.

Wrapping up

We defined the requirements and split the project into small chunks that we could develop & test one at-a-time.

The core functions are nice and simple – they each do just one job.

Each function runs a a short series of tests to make sure the input parameters are what we need them to be (e.g. $user_id needs to be greater than zero, $user needs to be a WP_User object, etc).

Have fun keeping unwanted users out of your site 😎 👍

Like This Tutorial?

Let us know

WordPress plugins for developers

Leave a comment