Cuttsy27
Cuttsy27

Reputation: 81

Only allow specific user roles to log into WordPress site

I currently have a WP site setup with a unique 'multisite' plugin installed specifically to allow for a single admin area of WooCommerce products, but with 2 different front-ends based on 2 different domains, with separate themes.

One of the sites is a 'wholesale' site while the other is 'retail'. The wholesale site should only allow trade customers to make purchases. The problem lies in that both sites are sharing a single domain, and therefore user accounts.

The problem: I need to ensure if a user who is does not have the user role 'trade_customer' tries to log into the wholesale site, the role is checked and the user is logged out, redirected to login page with a notification. So far I have the following in functions.php:

function trade_customers_only() {
    function get_user_role() {
    global $current_user;

    $user_roles = $current_user->roles;
    $user_role = array_shift($user_roles);

    return $user_role;
}

$the_current_role = get_user_role();
echo $the_current_role;
    if( $the_current_role != 'administrator' ) {
       $logout_url = wp_login_url().'?mode=tradeonly';
       wp_destroy_current_session();
       wp_logout();
       wp_redirect( $logout_url, 302 );
       exit();
    }     
}
 add_action('wp_login', 'trade_customers_only');

// CUSTOM LOGIN MESSAGES
function my_login_message() {

    if( $_GET['mode'] == 'tradeonly' ){
        $message = '<p class="message"><b>You must be a Trade Customer to access this site.</b></p>';
        return $message;
    }

}
 add_filter('login_message', 'my_login_message');

This code is currently: returning the logged in user to wp-login.php and adding the note "You must be a trade customer... etc". However, after the first login attempt with any user role, every other login attempt does the same redirect and shows message. Is my code incorrect or is there some WP session cookie in the DB or browser causing the problem whereby WP thinks I am not using an admin account?

The first time I attempted login was with admin account. it worked and went to dashboard. Next attempt was with a customer role account. The redirect and note occurred. A following attempt with admin account only did the redirect with note, but no dashboard access.

Upvotes: 1

Views: 4209

Answers (3)

Dale Mugford
Dale Mugford

Reputation: 21

I have tested the below code as working correctly.

There's no need to run wp_destroy_current_session() or wp_logout(), just simply return an error instead and it will interrupt authentication and show your error message on the login page.

You might have to make sure the priority is last (100 in this case), so that existing filters wp_authenticate_username_password, wp_authenticate_email_password and wp_authenticate_spam_check all do their thing and before the user is fully logged in that you will then deny.

/* Only allow administrators to login */
add_filter( 'authenticate', 'my_admin_check', 100, 1 );

function my_admin_check( $user ) {
    // Make sure this is a real login attempt, without errors
    if ( !is_wp_error($user) ) {
        $user_role = $user->roles[0];
        // add or remove the roles you want to allow/disallow (can be a custom role or regular WordPress roles)
        if ( !in_array( $user_role, array( 'trade_customer' ) ) ){
            return new WP_Error( 'login_failed', __( "Only staff can use this login.", "mysite_domain" ) );
        } else {
            // allow the login
            return $user;   
        }
    } else {
        // We're just loading the login page, not running a login.
        return $user;       
    }
}   

Upvotes: 0

Cuttsy27
Cuttsy27

Reputation: 81

My new full code is now:

function trade_customers_only($login, $user) {
    if( $user->roles && !in_array('administrator',$user->roles)) {
        $logout_url = wp_login_url().'?mode=tradeonly';
        wp_destroy_current_session();
        wp_logout();
        wp_redirect( $logout_url, 302 );
        exit();
    }
}
 add_action('wp_login', 'trade_customers_only',10,2);

// CUSTOM LOGIN MESSAGES
function my_login_message() {

    if( $_GET['mode'] == 'tradeonly' ){
        $message = '<p class="message"><b>You must be a Trade Customer to access this site.</b></p>';
        return $message;
    }

}
 add_filter('login_message', 'my_login_message');

This code works properly. However as Kulivov Sergey mentioned in his reply, using the authenticate filter instead of the wp_login action is better for what I need to achieve. Using:

add_filter('authenticate',function($user,$username) {
    if (!is_wp_error($user)) {
        $auth_user=get_user_by('login',$username);
        if ($auth_user && !in_array('administrator',$auth_user->roles)) {
            return new WP_Error('authentication_failed', '<p class="message"><b>You must be a Trade Customer to access this site.</b></p>');
        }
    }
    return $user;
},100,2);

Not only checks for the user role without logging in and creating a session, it also keeps the user on their current page with no redirection, which is great.

Upvotes: 0

Kulikov Sergey
Kulikov Sergey

Reputation: 265

1) Change your trade_customers_only function:

function trade_customers_only($login, $user) {
    if( $user->roles && !in_array('administrator',$user->roles)) {
        $logout_url = wp_login_url().'?mode=tradeonly';
        wp_destroy_current_session();
        wp_logout();
        wp_redirect( $logout_url, 302 );
        exit();
    }
}

And fix your action call:

add_action('wp_login', 'trade_customers_only',10,2);    

2) The other solution is using authenticate filter instead of using wp_login action. The difference is that you check for the user's role before user's session sets, so you don't need to destroy it.

add_filter('authenticate',function($user,$username) {
    if (!is_wp_error($user)) {
        $auth_user=get_user_by('login',$username);
        if ($auth_user && !in_array('administrator',$auth_user->roles)) {
            return new WP_Error('authentication_failed', '<p class="message"><b>You must be a Trade Customer to access Key Essentials. Are you looking for <a href="https://lovetillys.co.uk" title="Love Tillys">Love Tillys?</a></b></p>');
        }
    }
    return $user;
},100,2);

Upvotes: 4

Related Questions