php - "logoutOtherDevices" isn't working with Laravel Sanctum
i want to use Auth::logoutOtherDevices($currentPassword)
in laravel 8 to logout user from other devices after changing password. As per documentation ia have uncomment the line \Illuminate\Session\Middleware\AuthenticateSession::class,
. But unfortunately, it's not working. It says Method Illuminate\Auth\RequestGuard::viaRemember does not exist.
Can anyone please help me to sort it out how can i add logout from other devices functionality with the help of Auth::logoutOtherDevices($currentPassword)
or any manual method ?
Here is my code:
$rules = [
'currentpass' => 'required',
'newpass' => 'required|min:6',
'confnewpass' => 'required|same:newpass|min:6'
];
$messages = [
'currentpass.required' => 'Please enter your current password.',
'newpass.required' => 'Please provide a new password.',
'newpass.min' => 'Password must contain minimum 6 characters.',
'confnewpass.required' => 'Please provide your new password again to confirm.',
'confnewpass.same' => 'Both new passwords must be same.',
'confnewpass.min' => 'Password must contain minimum 6 characters.'
];
$this->validate($request, $rules, $messages);
$currentPass = $request->input('currentpass');
$newPass = $request->input('newpass');
try {
$user = User::findOrFail(Auth::id());
} catch (ModelNotFoundException $ex) {
$response['error'] = true;
$response['errors']['notFound'] = ['User Not Found.'];
return response()
->json($response, 400, [], JSON_PRETTY_PRINT);
}
if (!Hash::check($currentPass, $user->password)) {
return Redirect::back()
->withErrors(['Current Password', 'Please provide your current password properly.']);
}
$isChar = preg_match('/[a-zA-Z]+/', $newPass);
$isNum = preg_match('/\d+/', $newPass);
if (!($isChar && $isNum)) {
$response['error'] = 'Password must contain minimum 6 characters with at-least one letter and one number.';
return response()
->json($response, 200, [], JSON_PRETTY_PRINT);
}
/** hash password */
$hashpass = Hash::make($newPass);
$user->password = $hashpass;
try {
$user->save();
} catch (QueryException $ex) {
return Redirect::back()
->withErrors(['query', $ex->getMessage()]);
}
Auth::logoutOtherDevices($currentPass);
return Redirect::back()
->with('success', 'Your password has been successfully updated.');
Answer
Solution:
This is because Laravel Sanctum is using its own middleware, namely Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful
. While Auth::logoutOtherDevices($currentPassword)
needs \Illuminate\Session\Middleware\AuthenticateSession
to work.
To work around this, you can extend \Illuminate\Session\Middleware\AuthenticateSession
to works with Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful
. Here's the steps:
Create a new file
AuthenticateApiSession.php
inapp/Http/Middleware
(or any other folder you like). TheAuthenticateApiSession.php
should looks like this:<?php namespace App\Http\Middleware; use Closure; use Illuminate\Session\Middleware\AuthenticateSession; class AuthenticateApiSession extends AuthenticateSession { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { if (! $request->hasSession() || ! $request->user()) { return $next($request); } // Remove or comment this code block, or you'll get error. /*if ($this->auth->viaRemember()) { $passwordHash = explode('|', $request->cookies->get($this->auth->getRecallerName()))[2] ?? null; if (! $passwordHash || $passwordHash != $request->user()->getAuthPassword()) { $this->logout($request); } }*/ if (! $request->session()->has('password_hash')) { $this->storePasswordHashInSession($request); } if ($request->session()->get('password_hash') !== $request->user()->getAuthPassword()) { $this->logout($request); } return tap($next($request), function () use ($request) { $this->storePasswordHashInSession($request); }); } }
Add the newly created
App\http\Middleware\AuthenticateApiSession
class toapi
middleware group inapp/Http/Kernel.php
. Beware that it must be inserted afterEnsureFrontendRequestsAreStateful
.'api' => [ EnsureFrontendRequestsAreStateful::class, \App\Http\Middleware\AuthenticateApiSession::class, 'throttle:240,1', 'auth:airlock', 'branch.default', 'bindings', ],
That's it, now every ajax request to guarded API endpoint from other device should returns 401
response after Auth::logoutOtherDevices($currentPassword)
is called.
You should set your SPA (You're using sanctum, so I assume you're building an SPA) to handle invalid session in the ajax request though. Maybe redirecting the user to login page using javascript when encountering 401
ajax response.