Laravel Two Factor Authentication with Google Authenticator
Hello, today I’m going to share how to set up two-factor authentication with Google Authenticator in Laravel. I’m testing on Laravel 7.0.
I assume that you’ve installed a Laravel app & config basic things. If you didn’t do those things, you can check this article first.
Note: You’ve to install ImageMagick on your server if not installed. You may check this:
- Install ImageMagick (Imagick) PHP Extension on Windows
- Install ImageMagick (Imagick) Extension on RHEL / Centos 7
Table of Contents
- Install Required Package
- Create Model, Migration & Controller
- Setup Eloquent Relationship
- Setup Controller
- Create 2FA Middleware
- Define Routes
- Create Blade Files
- Run Project & Test 2FA
Install Required Package
We’ll use pragmarx/google2fa-laravel
package. Run this composer command to install this package:
composer require pragmarx/google2fa-laravel
After installation successful, run this command to publish the package’s config file:
php artisan vendor:publish --provider="PragmaRX\Google2FALaravel\ServiceProvider"
Create Model, Migration & Controller
We need a model, migration & controller for 2FA. Let’s create all using this command:
php artisan make:model LoginSecurity -m -c
Now open create_login_securities_table
migration file from database/migrations directory and update up()
function like this:
public function up()
{
Schema::create('login_securities', function (Blueprint $table) {
$table->id();
$table->integer('user_id');
$table->boolean('google2fa_enable')->default(false);
$table->string('google2fa_secret')->nullable();
$table->timestamps();
});
}
Run the migration to create tables:
php artisan migrate:refresh
Setup Eloquent Relationship
Open User model from app folder and insert this function:
public function loginSecurity()
{
return $this->hasOne(LoginSecurity::class);
}
Open LoginSecurity model and insert user()
function & $fillable
array:
protected $fillable = [
'user_id'
];
public function user()
{
return $this->belongsTo(User::class);
}
Setup Controller
Open from LoginSecurityController from app/Http/Controllers directory and paste this code:
<?php
namespace App\Http\Controllers;
use App\LoginSecurity;
use Auth;
use Hash;
use Illuminate\Http\Request;
class LoginSecurityController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show 2FA Setting form
*/
public function show2faForm(Request $request){
$user = Auth::user();
$google2fa_url = "";
$secret_key = "";
if($user->loginSecurity()->exists()){
$google2fa = (new \PragmaRX\Google2FAQRCode\Google2FA());
$google2fa_url = $google2fa->getQRCodeInline(
'MyNotePaper Demo',
$user->email,
$user->loginSecurity->google2fa_secret
);
$secret_key = $user->loginSecurity->google2fa_secret;
}
$data = array(
'user' => $user,
'secret' => $secret_key,
'google2fa_url' => $google2fa_url
);
return view('auth.2fa_settings')->with('data', $data);
}
/**
* Generate 2FA secret key
*/
public function generate2faSecret(Request $request){
$user = Auth::user();
// Initialise the 2FA class
$google2fa = (new \PragmaRX\Google2FAQRCode\Google2FA());
// Add the secret key to the registration data
$login_security = LoginSecurity::firstOrNew(array('user_id' => $user->id));
$login_security->user_id = $user->id;
$login_security->google2fa_enable = 0;
$login_security->google2fa_secret = $google2fa->generateSecretKey();
$login_security->save();
return redirect('/2fa')->with('success',"Secret key is generated.");
}
/**
* Enable 2FA
*/
public function enable2fa(Request $request){
$user = Auth::user();
$google2fa = (new \PragmaRX\Google2FAQRCode\Google2FA());
$secret = $request->input('secret');
$valid = $google2fa->verifyKey($user->loginSecurity->google2fa_secret, $secret);
if($valid){
$user->loginSecurity->google2fa_enable = 1;
$user->loginSecurity->save();
return redirect('2fa')->with('success',"2FA is enabled successfully.");
}else{
return redirect('2fa')->with('error',"Invalid verification Code, Please try again.");
}
}
/**
* Disable 2FA
*/
public function disable2fa(Request $request){
if (!(Hash::check($request->get('current-password'), Auth::user()->password))) {
// The passwords matches
return redirect()->back()->with("error","Your password does not matches with your account password. Please try again.");
}
$validatedData = $request->validate([
'current-password' => 'required',
]);
$user = Auth::user();
$user->loginSecurity->google2fa_enable = 0;
$user->loginSecurity->save();
return redirect('/2fa')->with('success',"2FA is now disabled.");
}
}
In the controller, we’ve set 2FA settings form, generate secret key function and enable & disable option.
Create 2FA Middleware
This package provides us with a middleware that can be directly used to enable two-factor authentication.
Go to the app folder and create a directory named Support. Under the support folder, create a file called Google2FAAuthenticator.php & paste this code:
<?php
namespace App\Support;
use PragmaRX\Google2FALaravel\Support\Authenticator;
class Google2FAAuthenticator extends Authenticator
{
protected function canPassWithoutCheckingOTP()
{
if($this->getUser()->loginSecurity == null)
return true;
return
!$this->getUser()->loginSecurity->google2fa_enable ||
!$this->isEnabled() ||
$this->noUserIsAuthenticated() ||
$this->twoFactorAuthStillValid();
}
protected function getGoogle2FASecretKey()
{
$secret = $this->getUser()->loginSecurity->{$this->config('otp_secret_column')};
if (is_null($secret) || empty($secret)) {
throw new InvalidSecretKey('Secret key cannot be empty.');
}
return $secret;
}
}
After that create a middleware named LoginSecurityMiddleware using this command:
php artisan make:middleware LoginSecurityMiddleware
Open the middleware from app/Http/Middleware and paste this code:
<?php
namespace App\Http\Middleware;
use App\Support\Google2FAAuthenticator;
use Closure;
class LoginSecurityMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$authenticator = app(Google2FAAuthenticator::class)->boot($request);
if ($authenticator->isAuthenticated()) {
return $next($request);
}
return $authenticator->makeRequestOneTimePasswordResponse();
}
}
Now open the Kernel.php from the app/Http directory & register our middleware to $routeMiddleware
array.
protected $routeMiddleware = [
// ---------
'2fa' => \App\Http\Middleware\LoginSecurityMiddleware::class,
];
Define Routes
Go to the routes folder, open web.php & define these routes:
Route::group(['prefix'=>'2fa'], function(){
Route::get('/','[email protected]');
Route::post('/generateSecret','[email protected]')->name('generate2faSecret');
Route::post('/enable2fa','[email protected]')->name('enable2fa');
Route::post('/disable2fa','[email protected]')->name('disable2fa');
// 2fa middleware
Route::post('/2faVerify', function () {
return redirect(URL()->previous());
})->name('2faVerify')->middleware('2fa');
});
// test middleware
Route::get('/test_middleware', function () {
return "2FA middleware work!";
})->middleware(['auth', '2fa']);
Create Blade Files
We’re about to finish. We need to create two view files only. Navigate to resources/views/auth directory and create 2 files named 2fa_settings.blade.php
and 2fa_verify.blade.php
.
Open 2fa_settings blade file and paste this code:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-md-center">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong>Two Factor Authentication</strong></div>
<div class="card-body">
<p>Two factor authentication (2FA) strengthens access security by requiring two methods (also referred to as factors) to verify your identity. Two factor authentication protects against phishing, social engineering and password brute force attacks and secures your logins from attackers exploiting weak or stolen credentials.</p>
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if($data['user']->loginSecurity == null)
<form class="form-horizontal" method="POST" action="{{ route('generate2faSecret') }}">
{{ csrf_field() }}
<div class="form-group">
<button type="submit" class="btn btn-primary">
Generate Secret Key to Enable 2FA
</button>
</div>
</form>
@elseif(!$data['user']->loginSecurity->google2fa_enable)
1. Scan this QR code with your Google Authenticator App. Alternatively, you can use the code: <code>{{ $data['secret'] }}</code><br/>
<img src="{{$data['google2fa_url'] }}" alt="">
<br/><br/>
2. Enter the pin from Google Authenticator app:<br/><br/>
<form class="form-horizontal" method="POST" action="{{ route('enable2fa') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('verify-code') ? ' has-error' : '' }}">
<label for="secret" class="control-label">Authenticator Code</label>
<input id="secret" type="password" class="form-control col-md-4" name="secret" required>
@if ($errors->has('verify-code'))
<span class="help-block">
<strong>{{ $errors->first('verify-code') }}</strong>
</span>
@endif
</div>
<button type="submit" class="btn btn-primary">
Enable 2FA
</button>
</form>
@elseif($data['user']->loginSecurity->google2fa_enable)
<div class="alert alert-success">
2FA is currently <strong>enabled</strong> on your account.
</div>
<p>If you are looking to disable Two Factor Authentication. Please confirm your password and Click Disable 2FA Button.</p>
<form class="form-horizontal" method="POST" action="{{ route('disable2fa') }}">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('current-password') ? ' has-error' : '' }}">
<label for="change-password" class="control-label">Current Password</label>
<input id="current-password" type="password" class="form-control col-md-4" name="current-password" required>
@if ($errors->has('current-password'))
<span class="help-block">
<strong>{{ $errors->first('current-password') }}</strong>
</span>
@endif
</div>
<button type="submit" class="btn btn-primary ">Disable 2FA</button>
</form>
@endif
</div>
</div>
</div>
</div>
</div>
@endsection
Then open 2fa_verify blade file and paste:
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-md-center">
<div class="col-md-8 ">
<div class="card">
<div class="card-header">Two Factor Authentication</div>
<div class="card-body">
<p>Two factor authentication (2FA) strengthens access security by requiring two methods (also referred to as factors) to verify your identity. Two factor authentication protects against phishing, social engineering and password brute force attacks and secures your logins from attackers exploiting weak or stolen credentials.</p>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Enter the pin from Google Authenticator app:<br/><br/>
<form class="form-horizontal" action="{{ route('2faVerify') }}" method="POST">
{{ csrf_field() }}
<div class="form-group{{ $errors->has('one_time_password-code') ? ' has-error' : '' }}">
<label for="one_time_password" class="control-label">One Time Password</label>
<input id="one_time_password" name="one_time_password" class="form-control col-md-4" type="text" required/>
</div>
<button class="btn btn-primary" type="submit">Authenticate</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Last task, open config/google2fa.php
file and set one-time password view file path:
/*
* One Time Password View.
*/
'view' => 'auth.2fa_verify',
This view will be shown to the user to enter OTP.
Run Project & Test 2FA
Our application is ready. Let’s run the project and test 2FA. After running the app, login to app and visit this route:
http://exmaple.com/2fa
If everything is okay, you’ll see a page like this:

Then click the “Generate Secret Key…” button. It’ll generate a key and display this info:

Open the Google Authenticator application and scan the QR code. Then the app will show a code. Enter the code to enable 2FA.
After entering the correct code, the 2FA will be enabled on your account.

Let’s test middleware: Logout and login again. Then visit this route:
http://exmaple.com/test_middleware
It’ll ask to provide OTP like:

That’s it. You’ll find more functionalities from pragmarx/google2fa-laravel‘s GitHub repo.
The tutorial is over. You can download this project from GitHub. Thank you. ?Comment
Preview may take a few seconds to load.
Markdown Basics
Below you will find some common used markdown syntax. For a deeper dive in Markdown check out this Cheat Sheet
Bold & Italic
Italics *asterisks*
Bold **double asterisks**
Code
Inline Code
`backtick`Code Block```
Three back ticks and then enter your code blocks here.
```
Headers
# This is a Heading 1
## This is a Heading 2
### This is a Heading 3
Quotes
> type a greater than sign and start typing your quote.
Links
You can add links by adding text inside of [] and the link inside of (), like so:
Lists
To add a numbered list you can simply start with a number and a ., like so:
1. The first item in my list
For an unordered list, you can add a dash -, like so:
- The start of my list
Images
You can add images by selecting the image icon, which will upload and add an image to the editor, or you can manually add the image by adding an exclamation !, followed by the alt text inside of [], and the image URL inside of (), like so:
Dividers
To add a divider you can add three dashes or three asterisks:
--- or ***

Comments (5)
Kushal
Hi, Thank you for the tutorial. It is awesome !
But i have to also implement recovery flow for google 2FA.
Do you have any guide/tutorial for that? Let us know.
Thanks.
Md Obydullah
Hi Kushal,
You're welcome. I didn't write article on Google 2FA recovery. I'll try to write an article on this.
Pedro D S Junior
After installation this code: composer require bacon/bacon-qr-code
my 2fa it is work
Md Obydullah
Hi Pedro D S Junior,
I'm sorry for the late response. Glad to hear that it works. And thanks for letting us know the solution. ?
Pedro D S Junior
Hi, thank you for your tutorial. Could you help me with this error?
You need to install a service package or assign yourself the service to be used.