Goderaftw
Goderaftw

Reputation: 159

Laravel 5 Logout Specific User

In my laravel 5 application there is a function to allow users with the admin role to reset passwords of anyone not an admin, however this does not force the person to logout and login again. How can I force the user to log out once their password has been changed? I have not made any changes to the middleware for authenticating users or anything.

Upvotes: 3

Views: 14917

Answers (7)

Joiner
Joiner

Reputation: 1

I've faced this problem in the past, @vkovic explained it very well, when using guard()->logout() destroys every guard session. My approach to solve this was different tho, I still used the default logout process but I restored the guard session I wanted to preserve.

 public function logout(Request $request)
{
    // save the teams sessions if exist
    if (auth('teams')->user()) {
        $teams_session_hash = 'login_teams_'.sha1('Illuminate\Auth\SessionGuard');
    }

    $this->guard()->logout();

    $request->session()->invalidate();
    $request->session()->regenerateToken();

    // restores the teams sessions if exist
    if (isset($teams_session_hash) && $teams_session_hash) {
        $request->session()->put($teams_session_hash, auth('teams')->id());
    }

    return $this->loggedOut($request) ?: redirect('/');
}

Upvotes: 0

Igor
Igor

Reputation: 11

$findUser = User::find($userId); \Session::getHandler()->destroy($findUser->session_id);

Laravel 5.5

Upvotes: 0

vkovic
vkovic

Reputation: 804

Trying to avoid additional complexity like adding fields to db, after little bit of investigation I came across next solution.

Idea is based around Laravel 5.4, but should be compatible with all 5.x releases.

The problem lies in way Laravel handles logout. As we can see in https://github.com/laravel/framework/blob/5.4/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php#L154

public function logout(Request $request)
{
    $this->guard()->logout();

    $request->session()->invalidate();

    return redirect('/');
}

The line $request->session()->invalidate(); is flushing request session data and regenerate the ID. So after this one, if we had multiple guards enabled, they all will be logged out.

The idea is to remove just one, session key which corresponds to the current user we are logging out. If we inspect our session (pay attention to "login_*" keys), while users from different guards are logged in, we'll get something like this:

array:5 [▼
    "_token" => "qB4zDqDbknpO7FOrfNQ3YuFxpnji95uamJflxSkV"
    "_previous" => array:1 [▶]
    "_flash" => array:2 [▶]
    "login_admin_51ba36addc2b2f9401580f014c7f58ea4e30989d" => 74
    "login_user_51ba36addc2b2f9401580f014c7f58ea4e30989d" => 23
]

Instead of flushing whole session, we just need to delete this single, corresponding key. To get current guard session name (session key in example above), we can use guard method: https://github.com/laravel/framework/blob/5.4/src/Illuminate/Auth/SessionGuard.php#L622

Now we have everything we need to perform this task. Here is the example from the project I'm currently on:

namespace App\Http\Controllers\Admin\Auth;

use Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    use AuthenticatesUsers;

    public function __construct()
    {
        $this->middleware('guest:admin', ['except' => 'logout']);
    }

    protected function guard()
    {
        return Auth::guard('admin');
    }

    public function logout()
    {
        // Get the session key for this user
        $sessionKey = $this->guard()->getName();

        // Logout current user by guard
        $this->guard()->logout();

        // Delete single session key (just for this user)
        $request->session()->forget($sessionKey);

        // After logout, redirect to login screen again
        return redirect()->route('admin.login');
    }

    // ... Other code ...

}

With LoginController::logout method we're overriding trait logout (default Laravel logout logic) with our custom, almost the same, but which will allow us to logout single user.

The same logic applies for all our login controllers depending on how much different guards we have.

I just finished this solution and after quick testing it seems to be working fine, but please inspect it carefully before implementing.

Upvotes: 2

Marcin Nabiałek
Marcin Nabiałek

Reputation: 111829

I don't know if it will work but you can try:

// get current user
$user = Auth::user();

// logout user
$userToLogout = User::find(5);
Auth::setUser($userToLogout);
Auth::logout();

// set again current user
Auth::setUser($user);

Upvotes: 13

Claudio King
Claudio King

Reputation: 1616

I think that the fastest solution is to add a flag to the users DB table, for example a boolean column to_logout and the in the Auth middleware add something like this code.

$user = Auth::user();

if($user->to_logout){
    Auth::logout();
    $user->update(['to_update' => 0]);

    return redirect('/');
}

Upvotes: 5

mister martin
mister martin

Reputation: 6252

Looking over the docs, it does not appear there is any built-in function for this and a similar request has been proposed which also describes the problem of tracking a single user who has multiple sessions open on more than one device.

I believe you will need to create a custom solution, for example (as @Dinar mentioned) if you are storing user sessions in a database then you could retrieve and destroy a specific user's session when a certain condition is met - changing their password.

Upvotes: 0

Dinar
Dinar

Reputation: 74

If you use Laravel 5.2, you can change session storage engine to Database. In this case every session record will also contain user's ID.

All you need is just to remove respective row from database.

Upvotes: 1

Related Questions