Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel - destroy existing sessions on login

Is there a way to check if a user already has a valid session on a different machine?

What I want to do is when a user logs in, destroy an other sessions which they may already have, so that if they forget to logout from a computer say on campus or at work, and then they log in at home, it will destroy those other 2 sessions so they are no longer logged in?

Facebook employs this in some way.

My only thoughts so far is something to this effect:

$user = User::find(1); // find the user
Auth::login($user); // log them in 
Auth::logout(); // log them out hoping that it will destroy all their sessions on all machines
Auth::login($user); // log them in again so they have a valid session on this machine

I have not had the chance to test this, and I do not know if Auth::login($user); will destroy all sessions for that user, or only the current one.

Thanks!

like image 614
Vigs Avatar asked Oct 22 '25 18:10

Vigs


2 Answers

You can save a session_id within a user model, so that:

  1. When logout event is fired (auth.logout) you would clear it.

  2. When new logging event is fired you can check if attribute session_id is not null within the user model.

  3. If it's not - destroy previous session by:

Session::getHandler()->destroy($user->session_id);

$user->session_id = Session::getId();

Hope that would help!

like image 152
technik Avatar answered Oct 24 '25 08:10

technik


I realise this is an old question, but there is now a method in laravel 5.6 that does exactly this, so it may be useful for someone coming to this later. You can also retro-fit this method to earlier versions of laravel very easily.

See the docs at https://laravel.com/docs/5.6/authentication#invalidating-sessions-on-other-devices

I had the same use case as you (log out all other devices on log-in). I overrode the default login method to add my own custom logic (first copying the default login method from vendor/laravel/framework/src/illuminate/Foundation/Auth/AuthenticatesUsers.php)

In that method, there is the line if ($this->attemptLogin($request)) - within this, before the return statement, add your call to logoutOtherDevices, as below

if ($this->attemptLogin($request)) {

        //log out all other sessions
        Auth::logoutOtherDevices($request->password); //add this line

        return $this->sendLoginResponse($request);
}

Also ensure you have un-commented the Illuminate\Session\Middleware\AuthenticateSession middleware in your app/Http/Kernel.php, as per the docs

(note that I haven't tested the above code as I was using an older version of laravel that doesn't have this method, see below). This should work in 5.6 though.

Older Laravel versions

I was actually using laravel 5.5, so didn't have access to this handy method. Luckily, it's easy to add. I opened a laravel 5.6 project and copied the logoutOtherDevices method from vendor/laravel/framework/src/illuminate/Auth/SessionGuard.php - for reference I have pasted below

/**
 * Invalidate other sessions for the current user.
 *
 * The application must be using the AuthenticateSession middleware.
 *
 * @param  string  $password
 * @param  string  $attribute
 * @return null|bool
 */
public function logoutOtherDevices($password, $attribute = 'password')
{
    if (! $this->user()) {
        return;
    }

    return tap($this->user()->forceFill([
        $attribute => Hash::make($password),
    ]))->save();
}

I then copied this into my LoginController - it could go somewhere else of your choice, but I've put it here for ease / laziness. I had to modify it slightly, as below ($this->user() becomes Auth::user())

 /**
 * Invalidate other sessions for the current user.
 * Method from laravel 5.6 copied to here
 *
 * The application must be using the AuthenticateSession middleware.
 *
 * @param  string  $password
 * @param  string  $attribute
 * @return null|bool
 */
public function logoutOtherDevices($password, $attribute = 'password')
{
    if (! Auth::user()) {
        return;
    }

    return tap(Auth::user()->forceFill([
        $attribute => Hash::make($password),
    ]))->save();
}

I can then call this method in my login method, as specified earlier in my answer, with a slight adjustment - $this->logoutOtherDevices($request->password);

If you want to test this locally, it seems to work if you open your site on a normal and an incognito window. When you log in on one, you'll be logged out on the other - though you'll have to refresh to see anything change.

like image 27
Roxy Walsh Avatar answered Oct 24 '25 08:10

Roxy Walsh