TransWikia.com

Limit number of sessions per user

Craft CMS Asked by cballenar on August 3, 2021

Is there a way to limit the number of sessions per user? That is, I can be logged into one machine/browsers only once.

It is essential that we limit the number of times a user can be logged in and I can’t seem to find a way to do so.

Has anyone dealt with this situation?

2 Answers

This can be done with a plugin.

The plugin would create an 'activity' table that saves the current user's ID and a timestamp of their last known activity.

You can insert the first row by listening to the onLogin event and remove their row by listening to the onLogout.

All subsequent requests, you'd compare the last known activity timestamp to the userSessionDuration config setting to still see if they have an active session.

You'd also be listening to the onBeforeLogin event. If someone tries to login on another computer, you'd see that they already have a row in the database and their last known activity timestamp is still in a valid userSessionDuration window. Then you'd pass $performAction = false as a property of the event, and that will prevent the login action from occurring.

Correct answer by Brad Bell on August 3, 2021

Im on Craft v3.4.25, and I've added the following event handler in my module to handle this. There are two scenarios here: either you want the new login to invalidate any existing session, or you want to block the new login if an active session exists.

If you want the new session to succeed, and log out any other sessions for that same user, you can do this:

Event::on(
    yiiwebUser::class,
    yiiwebUser::EVENT_BEFORE_LOGIN,
    function (UserEvent $event) {
        // remove all existing sessions for this user
        Session::deleteAll(['userId' => $event->identity->id]);
        return;
    }
);

This will remove all existing sessions for that user before logging them in with a new session.

If, instead, you want to block the user from logging in if another active session already exists, you could try this other method:

Event::on(
    yiiwebUser::class,
    yiiwebUser::EVENT_BEFORE_LOGIN,
    function (UserEvent $event) {
        // if this is not a CP request...
        if(!Craft::$app->getRequest()->getIsCpRequest()){
            $session = Session::find($event->identity->id)
                ->orderBy('dateUpdated desc')
                ->one();
            // if there is a session, and it was active within the last 'userSessionDuration' seconds...
            // do not let user login (only one user login at a time allowed)
            if($session && DateTimeHelper::isWithinLast($session->dateUpdated, $event->duration.' seconds')){
                $event->isValid = false;
                // temporarily override the login failure message for this request
                Event::on(
                    UsersController::class,
                    UsersController::EVENT_LOGIN_FAILURE,
                    function (LoginFailureEvent $event) {
                        $event->message = "This account is already logged in at another location.";
                    }
                );
                return;
            }
        }
    }
);

The UserEvent has a variable $duration which I believe is set by the general config variable userSessionDuration, which is in seconds.

For non-CP requests, this method should only allow a single login for a user account, at one time.

I added another event handler on UsersController::EVENT_LOGIN_FAILURE to set a custom error message when a login fails due to another active session for that user.

Seems to be working so far... hope this helps someone else. Feel free to poke holes!

Answered by Gary Reckard on August 3, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP