Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

My 2FA Code Has Usurped My Remeber Functionality. What is the Best Way to Address That?

The following my my current login controller. I added two-factor authentication (2FA), but that has killed my "Remember" functionality. 2FA is required by default for all users.

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 in session
        $request->session()->put('user_id', $user->id);

        // Redirect to the 2FA verification page
        return redirect()->route('two-factor.index');


        // If 2FA is not enabled, proceed with regular authentication
        Auth::login($user, $request->filled('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);
    }

How should I modify the code to re-enable the "remember" functionality and overall improve this method?

0 likes
1 reply
LaryAI's avatar
Level 58

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:

  1. The login method handles the initial login attempt and redirects to the 2FA verification page.
  2. The verifyTwoFactor method 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.

Please or to participate in this conversation.