benbyf

restrict same user session

Recommended Posts

Is there a way to restrict logins for users so that one user can't be loggedin in two places at the same time?

e.g. auto logout user after inactivity (of say 15 minutes..?), or logout action and disallow login if user still "logged in" somewhere?

Share this post


Link to post
Share on other sites

What do you mean by "logged in at two places at the same time"? How can user login again if it's already logged in? Am I missing something? 

Share this post


Link to post
Share on other sites
3 hours ago, abdus said:

What do you mean by "logged in at two places at the same time"? How can user login again if it's already logged in? Am I missing something? 

Maybe he means in 2 different browsers? I know sometimes I have a dev site open in chrome and opera so I can be on two different pages in the admin. 

  • Like 1

Share this post


Link to post
Share on other sites

 

17 minutes ago, louisstephens said:

2 different browsers

Ah, I totally missed that.

4 hours ago, benbyf said:

auto logout user after inactivity

There's a config field that lets you define session timeout.

/**
 * Session expiration seconds
 * 
 * How many seconds of inactivity before session expires
 * 
 * @var int
 *
 */
$config->sessionExpireSeconds = 86400;

 

4 hours ago, benbyf said:

disallow login if user still "logged in" somewhere?

That would be tricky to implement and probably not be secure at all. But using $input->whitelist you can keep sending and receiving a session id which can be used to identify a session across browsers. Google "cookieless session php".

https://stackoverflow.com/questions/5882401/cookie-less-sessions-in-php
https://stackoverflow.com/questions/14229193/sharing-session-between-different-browsers

I tried playing with $config->sessionFingerprint, but I couldn't make it work as you asked

/**
 * Use session fingerprint?
 * 
 * Should login sessions be tied to IP and user agent?
 * IP fingerprinting may be problematic on dynamic IPs. 
 * Below are the possible values: 
 * 
 * 	0 or false: Fingerprint off
 * 	1 or true: Fingerprint on with default/recommended setting (currently 10). 
 * 	2: Fingerprint only the remote IP
 * 	4: Fingerprint only the forwarded/client IP (can be spoofed)
 * 	8: Fingerprint only the useragent
 * 	10: Fingerprint the remote IP and useragent (default)
 * 	12: Fingerprint the forwarded/client IP and useragent
 * 	14: Fingerprint the remote IP, forwarded/client IP and useragent (all). 
 * 
 * If using fingerprint in an environment where the user’s 
 * IP address may change during the session, you should
 * fingerprint only the useragent, or disable fingerprinting.
 *
 * If using fingerprint with an AWS load balancer, you should 
 * use one of the options that uses the “client IP” rather than 
 * the “remote IP”, fingerprint only the useragent, or disable 
 * fingerprinting.
 * 
 * @var int
 *
 */
$config->sessionFingerprint = 1;

 

  • Like 1

Share this post


Link to post
Share on other sites
5 hours ago, benbyf said:

Is there a way to restrict logins for users so that one user can't be loggedin in two places at the same time?

e.g. auto logout user after inactivity (of say 15 minutes..?), or logout action and disallow login if user still "logged in" somewhere?

Maybe how PW checks if the same page is being edited in a different window could be of help? Have a look at the method checkProcessKey() in SystemNotifications module.

  • Like 2

Share this post


Link to post
Share on other sites
38 minutes ago, abdus said:

I tried playing with $config->sessionFingerprint, but I couldn't make it work as you asked

Amazing, I'll check this out. My question would be through would it still allow one user logged in using two fingerprints (e.g. logged in on my phone and laptop)?

21 minutes ago, kongondo said:

Maybe how PW checks if the same page is being edited in a different window could be of help? Have a look at the method checkProcessKey() in SystemNotifications module.

Sounds promising.

Share this post


Link to post
Share on other sites

OK, so I had a stab at this using $cache and ready.php. It seems to work fine.

Throw the following code in ready.php or similar...The code and in-line comments are purposefully verbose to make it easy to follow.

// Hook into login/logout sessions
wire()->addHookAfter('Session::loginSuccess', null, 'checkLoggedIn');
wire()->addHookBefore('Session::logout', null, 'removeLoggedIn');// Hook before to get $user->id

/**
 * Check if a user is already logged in
 *
 * If user logged in, take an action (notify,logout,etc).
 * Else, cache user as logged in to check for duplicate logins.
 * 
 * @param HookEvent $event The object (Session::loginSuccess) we are hooking into.
 * @return void
 * 
 */
function checkLoggedIn(HookEvent $event) {
    
    $user = $event->arguments('user');
    $session = wire('session');

    $userDuplicateLogin = checkUserDuplicateLogin($user);// returns boolean
    // if user logged in, do something. Here, we log them out and redirect to home page
    // you could make an exception for Superusers, or exception by role, permission, etc
    if($userDuplicateLogin) {
        $session->logout();
        $session->redirect('/');
     }
     // set cache
     else setLoggedInUserCache($user);
    /* @note: testing only
    $log = wire('log');
    $log->save("user-logs","Successful login for '$user->name'"); */

}

/**
 * Check if a user is logged in more than once.
 *
 * @param User $user The user to whose logins to check.
 * @return Boolean $duplicateLogIn True if user already logged in, else false.
 * 
 */
function checkUserDuplicateLogin(User $user) {    
    $cache = wire('cache');
    $duplicateLogIn = false;
    $userID = $user->id;    
    $cachedUsersIDs = $cache->get('loggedInUserIDs');// array OR null
    if(is_array($cachedUsersIDs) && isset($cachedUsersIDs[$userID])) $duplicateLogIn = true;
    return $duplicateLogIn;
}

/**
 * Create or update cache for logged in user.
 *
 * @param User $user The user whose cache to set.
 * @return void
 * 
 */
function setLoggedInUserCache(User $user) {
    
    $cache = wire('cache');
    $userID = $user->id; 

    $cachedUsersIDs = $cache->get('loggedInUserIDs');
    // cache does not exist, create empty array ready to cache
    if(!count($cachedUsersIDs) || is_null($cachedUsersIDs)) $cachedUsersIDs = array();

    // save/update cache
    // for value, can use whatever, even $user->name; doesn't matter, key is the important thing here 
    // outer array: we use $user->id to group same user;
    // in inner array, we use session_id() to ensure uniqueness when removing cache
    $cachedUsersIDs[$userID][session_id()] = $userID;
    $cachedUsersIDsStr = json_encode($cachedUsersIDs);// JSON to save as cache
    $cache->save('loggedInUserIDs',$cachedUsersIDsStr);// @note: set expiration of cache if you wish to
    
}

/**
 * Remove a logged out user's cache.
 * 
 * This to allow subsequent logins.
 *
 * @param HookEvent $event The object (Session::logout) we are hooking into.
 * @return void
 * 
 */
function removeLoggedIn(HookEvent $event) {
    $user = wire('user');
    removeLoggedInUserCache($user);
    /* @note: for testing only
     $log = wire('log');
    $log->save("user-logs","Successful logout for '$user->name'"); */
}

/**
 * Remove the cache for a user who has logged out.
 *
 * @param User $user The user whose logged-in cache we are removing.
 * @return void
 * 
 */
function removeLoggedInUserCache(User $user) {

    $cache = wire('cache');
    $userID = $user->id;   
    
    $cachedUsersIDs = $cache->get('loggedInUserIDs');

    // cache does not exist/empty, nothing to do
    if(!count($cachedUsersIDs) || is_null($cachedUsersIDs)) return;      

    // save/update cache
    // @note: we check for current logged in user but we remove the whole user group (outer array)
    // this is because the user logged in 'validly' is logging out.
    if(isset($cachedUsersIDs[$userID][session_id()])) unset($cachedUsersIDs[$userID]);   
    $cachedUsersIDsStr = json_encode($cachedUsersIDs);
    // save updated cached
    $cache->save('loggedInUserIDs',$cachedUsersIDsStr);// @note: set expiration of cache if you wish to

}

 

Edited by kongondo
  • Like 2

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By alan
      Sorry folks if this is obvious to most and I missed something in the docs or here in the forum, but, I have cheerfully used code such as:
      if($user->isLoggedin()) { echo " loggedIn"; } else { echo " notLoggedIn"; } in PW 2.x sites.
      Now on a v3 site I am finding even the most basic test to see if the viewer is logged in or `isSuperuser()` is not giving an error but simply doing nothing - not evaluating.
      For example, the top of a typical template reads:
      <?php namespace ProcessWire; if($user->isLoggedin()) { echo " loggedIn"; } else { echo " notLoggedIn"; } I have `debug` turned on.
      Is there something obvious I have missed, perhaps a step required for v3 that's not required for v2 to allow access to $user data?
      Thanks for any pointers.
    • By mcwhitey
      Hi,
      How can I display, on every page of my site, what roles can view that pages template, based on what is checked in settings>templates>template-name>ACCESS TAB?
      I've only gotten as far as displaying the name of the current template: echo $page->template->get('name'); But I'm stuck here. Tried things like $page->template->get('permissions'); but I guess it's a bit more complicated than that.
      Hope someone van point me in the right direction.
      Cheers.
    • By SwimToWin
      In my world, Super Users / Editors should only be able to work with Pages and administer users.
      Everything else is the domain of the web master. The purpose is to prevent technically inexperienced editors from destroying core elements of a site, such as fields and templates.
      That leaves me with the question, how to deny Super Users access to Setup, Modules and Roles / Permissions?
      PS: May I also suggest that it shall be possible to set Permission for each of the main menu items - including their sub-menu items.

    • By datomtom
      Being a newbie in ProcessWire I was wondering, whether I could have simple subdirectories on my webserver (serving specific self-developed php-apps) and use PW's built-in user management, to grant or deny access to those directories for specific users and groups. I was trying to wrap my head around LDAP for this, but it's not too easy to install on virtual servers running Plesk from my experience. So I thought I could possibly use PW's built in mechanisms for this purpose. Any ideas? Thanks in advance to the community!
    • By Martin Muzatko
      Hello there!
      I want to create a user front-end (user can register/login/logout via templates)
      I'm working based on the intermediate site profile. So _init.php is loaded first, then the template file and then _main.php.
      I integrated the custom login as described here, and changed it to my needs. ( I don't want to redirect the user, if the form is filled in successfully)
      The problem I face, is that $user->isLoggedin() lags behind $session->login().
      Which means that whenever I do a login, I DO get the information that the login was successful ($session->login(...) instanceof User). I COULD use that information on _main.php to show a profile in the upper right corner.
      However I don't want to set a variable in the template and ask for it in _main.php. Are there any alternatives? Is a redirect really required to complete the session handling? Why? I have the same problem for the logout. The user is still displayed as logged in, when he opens the logout page.
      Thanks in advance.
      Best,
      Martin