When a user logged out from a perticular device I want to logout from all the device he has logged in till now . How I do it in Laravel.
I have used Redis for keeping the userId in Session by installing "predis/predis": "~1.0"
And Here is my controller for SignIn and Logout:
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' =>$request['password'] ]) ) {
$redis = \Redis::connection();
$userId=Session::getId();
$redis->sadd('users:sessions:'.$userId,Session::getId());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
$redis = Redis::connection();
$userId=Session::getId();
$userSessions = $redis->smembers('user:sessions:' . $userId);
$currentSession = Session::getId();
foreach ($userSessions as $sessionId) {
if ($currentSession == $sessionId) {
continue;
}
$redis->srem('user:sessions:' . $userId, $sessionId);
$redis->del('laravel:' . $sessionId);
}
Auth::logout();
return redirect()->route('main');
}
It's successfully get logged in and also logged out but it doesn't kill all the session in other devices.
How do I solve the problem?
So issue was with typo in redis key name,
for write data used
$redis->sadd('users:sessions:'.$userId,Session::getId());
where key's prefix 'users:sessions:'
and for get data used
$redis->srem('user:sessions:' . $userId, $sessionId);
where key's prefix 'user:sessions:'
Thats why code didn't work and dd()
returned empty array.
so correct code looks like this
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' =>$request['password'] ]) ) {
$redis = \Redis::connection();
$userId=Session::getId();
$redis->sadd('user:sessions:'.$userId,Session::getId());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
$redis = Redis::connection();
$userId=Session::getId();
$userSessions = $redis->smembers('user:sessions:' . $userId);
$currentSession = Session::getId();
foreach ($userSessions as $sessionId) {
if ($currentSession == $sessionId) {
continue;
}
$redis->srem('user:sessions:' . $userId, $sessionId);
$redis->del('laravel:' . $sessionId);
}
Auth::logout();
return redirect()->route('main');
}
I have a suggestion/workaround for Your problem:
Working with sessions which are not stored in database is pain in a**, so You have to think differently in order to solve the problem. The different solution would be to record user id and time, when user logout. Then create middleware which will disconnects user if connection is older than last logout date. And thats all.
My prototype would look like so:
In postSignIn
method line below will record user (session) login date: app('request')->session()->put('login_date', time());
In getLogout
method line below will record user logout date globally:
\Cache::put('last_logout_'.\Auth::id(), time());
And final touch would be middleware with code similar to this:
if ($user = \Auth::user()) {
$login_date = app('request')->session()->get('login_date');
$last_logout_date = \Cache::get('last_logout_' . $user->id, time() + 100);
if ($login_date < $last_logout_date) {
\Auth::logout();
//redirect, error message...
}
}
Full code:
Methods:
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' => $request['password']])) {
app('request')->session()->put('login_date', time());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
\Cache::put('last_logout_' . \Auth::id(), time());
Auth::logout();
return redirect()->route('main');
}
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class LogoutIfExpired
{
public function handle($request, Closure $next, $guard = null)
{
if ($user = \Auth::user()) {
$login_date = app('request')->session()->get('login_date');
$last_logout_date = \Cache::get('last_logout_' . $user->id, time() + 100);
if ($login_date < $last_logout_date) {
\Auth::logout();
return redirect()->route('main');
}
}
return $next($request);
}
}