antonioribeiro / google2fa-laravel

A One Time Password Authentication package, compatible with Google Authenticator for Laravel
MIT License
920 stars 181 forks source link

Verification OTP successful but could not redirect to home page #186

Open nicholas-wcy opened 10 months ago

nicholas-wcy commented 10 months ago

I want to implement google authenticator to my app so after user logins, they will be prompt to enter the TOTP.

So far, I have followed the package documentation and install it as well as setting up the middleware,etc.

Here is my login method:

// existing logic above

if ($userInfo["login_attempt"] < 5 || $allowLogin == true) {

                if ($this->attemptLogin($request)) {
                    if ($request->hasSession()) {
                        $request->session()->put('auth.password_confirmed_at', time());
                    }
                    $userInfo->update(array("login_attempt" => 0, "unlock_login_attempt"=> null, "status" => 1));

                    if (!$userInfo->google2fa_secret) {
                        $google2fa = app('pragmarx.google2fa');

                        $google2fa_secret = $google2fa->generateSecretKey();
                        $qrCodeUrl = $google2fa->getQRCodeInline(
                            "app name",
                            $userInfo->email,
                            $google2fa_secret
                        );

                        $userInfo->update([
                            'google2fa_secret' => $google2fa_secret
                        ]);

                        session()->put('qrCodeUrl', $qrCodeUrl); // Store $test in the session

                        return redirect()->route('2fa');
                    }

                    return $this->sendLoginResponse($request);
                }

My auth.google2fa.show.blade.php:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2FA Verification</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>

<body>

    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">2FA Verification</div>

                    <div class="card-body">
                        <form method="POST" action="/2fa">
                            @csrf
                            <div class="form-group">
                                <label for="one_time_password">Enter 6-digit OTP from your Authenticator App</label>
                                <input id="one_time_password" type="text"
                                    class="form-control @error('one_time_password') is-invalid @enderror"
                                    name="one_time_password" required autofocus>

                                @error('one_time_password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>

                            <div class="form-group">
                                <button type="submit" class="btn btn-primary">
                                    Authenticate
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
                <!-- Add the QR code below the form -->
                @if (session()->has('qrCodeUrl'))
                    <div class="mt-4 text-center">
                        <p>Scan this QR code with your authenticator app:</p>
                        {!! session('qrCodeUrl') !!}
                    </div>
                @endif
            </div>
        </div>
    </div>

</body>

</html>

TwoFactorController.php :

<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use PragmaRX\Google2FALaravel\Facade as Google2FA;

class TwoFactorController extends Controller
{
    public function show()
    {
        return view('auth.google2fa.show'); // Use the appropriate view for your 2FA verification page
    }

    public function verify(Request $request)
    {
        $validated = $request->validate([
            'one_time_password' => 'required',
        ]);

        $user = Auth::user();
        $google2fa = app('pragmarx.google2fa');
        $valid = $google2fa->verifyKey($user->google2fa_secret, $request->input('one_time_password'));
        // Verify the 2FA code using the package's functionality
        if ($valid) {
            // 2FA code is valid, proceed with login

            return redirect()->route('admin.dashboard');
        } else {
            // Invalid 2FA code, redirect back with an error message
            return redirect()->route('2fa')->withErrors(['one_time_password' => 'Invalid OTP code']);
        }
    }
}

Config google2fa.php:


<?php

return [

    /*
     * Enable / disable Google2FA.
     */
    'enabled' => env('OTP_ENABLED', true),

    /*
     * Lifetime in minutes.
     *
     * In case you need your users to be asked for a new one time passwords from time to time.
     */
    'lifetime' => env('OTP_LIFETIME', 0), // 0 = eternal

    /*
     * Renew lifetime at every new request.
     */
    'keep_alive' => env('OTP_KEEP_ALIVE', true),

    /*
     * Auth container binding.
     */
    'auth' => 'auth',

    /*
     * Guard.
     */
    'guard' => '',

    /*
     * 2FA verified session var.
     */
    'session_var' => 'google2fa',

    /*
     * One Time Password request input name.
     */
    'otp_input' => 'one_time_password',

    /*
     * One Time Password Window.
     */
    'window' => 1,

    /*
     * Forbid user to reuse One Time Passwords.
     */
    'forbid_old_passwords' => false,

    /*
     * User's table column for google2fa secret.
     */
    'otp_secret_column' => 'google2fa_secret',

    /*
     * One Time Password View.
     */
    'view' => 'auth.google2fa.show',

    /*
     * One Time Password error message.
     */
    'error_messages' => [
        'wrong_otp'       => "The 'One Time Password' typed was wrong.",
        'cannot_be_empty' => 'One Time Password cannot be empty.',
        'unknown'         => 'An unknown error has occurred. Please try again.',
    ],

    /*
     * Throw exceptions or just fire events?
     */
    'throw_exceptions' => env('OTP_THROW_EXCEPTION', true),

    /*
     * Which image backend to use for generating QR codes?
     *
     * Supports imagemagick, svg and eps
     */
    'qrcode_image_backend' => \PragmaRX\Google2FALaravel\Support\Constants::QRCODE_IMAGE_BACKEND_SVG,

];

web.php:

Route::group([
    'middleware' => ['auth'],
], function () {
    Route::get('/2fa', [TwoFactorController::class, 'show'])->name('2fa');
    Route::post('/2fa', [TwoFactorController::class, 'verify'])->name('verify-2fa');
});

Route::group([
    'middleware' => ['auth', '2fa'],
    'as'         => 'admin.',
], function () {
    Route::get('/', [DashboardController::class, 'index'])
        ->name('dashboard');
.......

While the flow and logic works fine up until the verification OTP, when I enter a valid OTP, it verifies successfully but it does not redirect to the home dashboard. It just redirects back to the OTP page. I assumed is because it somehow did not "pass" the 2fa middleware in my routes. I am not sure why is this the case or if I am missing something as i could not find a solution online for my issue.

Does anyone know the issue?

ghost commented 5 months ago

If you haven't found the solution, you needed to invoke the Google2FA::login() after a successful verification

image

realtebo commented 4 months ago

I got

Call to undefined method PragmaRX\Google2FA\Google2FA::login()

The login method is from which class?

arzlo commented 3 months ago

I got

Call to undefined method PragmaRX\Google2FA\Google2FA::login()

The login method is from which class?

You need to call the class:

use Google2FA;