I want to add google authenticator TOTP in my laravel app. I am using this package pragmarx/google2fa-laravel
Link to package: https://github.com/antonioribeiro/google2fa-laravel
Login controller:
`.....
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('admin.2fa');
}
return $this->sendLoginResponse($request);
}
`
2FA page (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="">
@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">
Verify OTP
</button>
</div>
</form>
</div>
</div>
<!-- Add the QR code below the form -->
<div class="mt-4 text-center">
<p>Scan this QR code with your authenticator app:</p>
{!! session('qrCodeUrl') !!}
</div>
</div>
</div>
</div>
</body>
</html>
`
TwoFactorController:
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']);
}
}
}
My web.php:
Route::group([
'middleware' => ['auth'],
], function () {
Route::get('/2fa', [TwoFactorController::class, 'show'])->name('2fa-page');
Route::post('/2fa', [TwoFactorController::class, 'verify'])->name('verify-2fa');
});
Route::group([
'middleware' => ['auth', '2fa'],
'as' => 'admin.',
], function () {
Route::get('/', [DashboardController::class, 'index'])
->name('dashboard');
....
After user successfully login (email and password), they will be redirected to a 2FA page where they can scan the QR code and input the OTP on the form. After that, upon successful OTP verification they will be redirected to the dashboard home page. My flow seems to work right up until the successful verification. When the OTP is succesful, it could not redirect to the home page and instead keeps redirecting back to the 2FA page.
I am not sure what is the issue as I have look through the documentation for the package but there is not a solution to my issue. My guess is it somehow still did not "pass" the 2fa middleware, hence redirected back to the 2FA page.
I tried removing the 2FA middleware in the route and it would work but that defeats the purpose of middleware as anyone can easily bypass the 2FA challenge by changing the url to another page.
But then what happens next? where is this value saved? How can we do a check if 2fa has been verified? The middleware fails at every point so I will rather make my own but still how do we know where to pull if the user was verified?
Seems the documentation just ends with no clue of what to do next.
Any help / insight would be greatly appreciated.