How to add two factor authentication to your Laravel 5 application in 6 steps

When ever a user logs into your app and tries to access a restricted area we want to force a two factor authentication. Optionally this approach will allow you to have areas that require two factor and other areas that don’t if you wish.

We also want the session to last on the browser they are on for the same amount of time as Laravel’s set session lifetime. You can find your session lifetime variable in config\sessions.php its default value is 120 minutes. This means you will be able to access to the backend with out needing to login for 120 minutes, if you log out however you will be forced to two-factor again.

What we need

This feature can be accomplished entirely with our own app and email, if we want to use SMS we will need Twillio or a similar alternative.

I’m going to assume you’re using the default authentication views and logic that comes with Laravel, and we will extend off of it.

Step 1 — Adding your two factor form to the view.

Let’s add a new view, just copy and paste the login form view and replace its form with something like this, then save it as two_factor.blade.php

<form role="form" method="POST" action="/2fa">
{{ csrf_field() }}
<div class="form-group {{ $errors->has('email') ? 'has-error' : '' }}">
<input id="2fa" type="text" class="form-control" name="2fa" placeholder="Enter the code you received here." required autofocus>
@if ($errors->has('2fa'))
<span class="help-block">
<strong>{{ $errors->first('2fa') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Send</button>
</div>
</form>

Step 2 — Database migrations

You need to make sure your users table has a phone_number or email column. Any user you want to authenticate for a certain section of your site will need to have a phone number or email associated with the account so you can ask them to verify. Just add this to your create_users_table migration:

$table->string('email');
$table->string('phone_number');
$table->string('token_2fa')->nullable();
$table->datetime('token_2fa_expiry')->nullable();

Step 3 — Auth Controller

Now we are going to extend the default Laravel authentication controller found in App\Http\Controllers\Auth\LoginController.php .. Just add the authenticated method to the LoginController.php ..

Let’s set the two factor token to be expired so that when we redirect to the /admin view (which is the section we want to protect with two-factor — change this route to what ever route you want protected with 2FA) our middleware can handle an expired authentication token.

Step 4 — Middleware

Create a new middleware called TwoFactorVerify.php you can do this by issuing an artisan command to the CLI using php artisan make:middleware TwoFactorVerify and then you’ll just need to replace the handle method with this one:

PS you will need to Use Auth; at the top of this class. Also you will need to install aloha/twilio package if you want to use twilio like I am, see his directions to install properly.

Let’s add our new middleware to our admin middleware group in the App\Http\Kernel.php file. This means anything in the route group admin will require this middleware.

'admin' => [
\App\Http\Middleware\TwoFactorVerify::class,
],

If you plan on using the two factor middleware outside of the admin group add it to the route middleware array as well.

protected $routeMiddleware = [
'two_factor' => \App\Http\Middleware\TwoFactorVerify::class,
];

Then anytime you want to check to see if the user is two factor verified you just assign this middleware to the route, group or even in the controller constructor as middleware.

Step 5 — Routes

We need to add our two factor routes to the web routes file:

// Two Factor Authentication
Route::get('2fa', 'TwoFactorController@showTwoFactorForm');
Route::post('2fa', 'TwoFactorController@verifyTwoFactor');

Step 6 — Two Factor Controller

We need a new controller called TwoFactorController.php inside this controller we will add these methods. The first method is the POST function for the form we created in step 1. The second is just the method to render the two factor form view.

Let’s break this down:

The first part is just a form validation ensuring there is a value. The second part is an if statement that is ensuring that the input the user gave matches the users token we created in the middleware, which was saved to the users row in the database when the user attempted to hit the /admin route. If it passes then lets grab the user, update the token_2fa_expiry to the same amount of time as our app/config/session setting, then save the user and redirect to the/admin route where it will load the view because the user has been successfully two factor authenticated.

If the incorrect code was entered just return to the two_factor and let the user know it was incorrect. Keep in mind the user is authenticated, its just our 2FA middleware that is preventing the user from accessing the admin section because its token expiry date is currently less then now, once the correct code gets entered the user will be granted the session.

That’s all there is to it.

— — — — — — —

Want to work together? I design and code at my company Firescript feel free to drop by and say hey via drift. Not your style? I’m on Twitter, Instagram & Facebook as well.