To address the issue of the "Remember" functionality being overridden by the 2FA process, you need to ensure that the "remember" token is set after the 2FA verification is successful. The current flow redirects to the 2FA verification page before setting the "remember" token, which is why it doesn't work as expected.
Here's a revised version of your login method that separates the initial login attempt from the 2FA verification. This way, the "remember" token is set only after the user successfully completes the 2FA process.
First, update your login method to handle the initial login and redirect to the 2FA verification page:
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $credentials['email'])->first();
if (!$user || !Hash::check($credentials['password'], $user->password)) {
return back()->withErrors(['email' => 'The provided credentials do not match our records.']);
}
if ($user->locked) {
return back()->withErrors(['email' => 'Your account is LOCKED. Please contact an administrator for assistance.']);
}
// Generate a random 2FA code
$user->twofactor_code = rand(100000, 999999);
$user->twofactor_expires_at = now()->addMinutes(10);
$user->save();
// Send the 2FA code via SMS
$user->notify(new TwoFactorCodeNotification($user->twofactor_code));
// Store the user ID and remember status in session
$request->session()->put('user_id', $user->id);
$request->session()->put('remember', $request->filled('remember'));
// Redirect to the 2FA verification page
return redirect()->route('two-factor.index');
}
Next, create a method to handle the 2FA verification:
public function verifyTwoFactor(Request $request)
{
$request->validate([
'twofactor_code' => 'required|numeric',
]);
$user = User::find($request->session()->get('user_id'));
if (!$user || $user->twofactor_code !== $request->twofactor_code || $user->twofactor_expires_at->lt(now())) {
return back()->withErrors(['twofactor_code' => 'The provided 2FA code is invalid or has expired.']);
}
// Clear the 2FA code
$user->twofactor_code = null;
$user->twofactor_expires_at = null;
$user->save();
// Log the user in and set the remember token if applicable
Auth::login($user, $request->session()->get('remember'));
$user->update([
'last_login_dt' => now(),
'last_login_ip' => $request->ip(),
'logins' => $user->logins + 1,
]);
$this->logUserActivity($user, $request->ip());
$request->session()->regenerate();
$message = 'Welcome back, ' . $user->firstname . ' ' . $user->lastname . '! ' .
'You last logged in on ' . $user->last_login_dt->format('Y-m-d H:i:s');
return redirect()->intended('home')->with('message', $message);
}
In this setup:
- The
loginmethod handles the initial login attempt and redirects to the 2FA verification page. - The
verifyTwoFactormethod handles the 2FA code verification, logs the user in, and sets the "remember" token if applicable.
Make sure you have a route and a view for the 2FA verification page. The route might look something like this:
Route::get('two-factor', [LoginController::class, 'showTwoFactorForm'])->name('two-factor.index');
Route::post('two-factor', [LoginController::class, 'verifyTwoFactor'])->name('two-factor.verify');
And the showTwoFactorForm method in your LoginController:
public function showTwoFactorForm()
{
return view('auth.two-factor');
}
This approach ensures that the "remember" functionality is preserved and only set after the user successfully completes the 2FA process.