2FA in Laravel with Google Authenticator – Get Secure! – SitePoint

This article was peer reviewed by Jad Bitar, Niklas Keller, Marco Pivetta, and Anthony Chambers. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
vector image with lock and digital symbols, indicating security
There are many ways an attacker can get a user’s password. It could happen through social engineering, key logging, or by some other nefarious means. Passwords alone are not enough to protect users from their accounts being compromised, specially if an attacker somehow has their credentials.
To get around this serious shortcoming, the concept of two-factor authentication (2FA) was created. A password, which is one factor, is not enough to authenticate a user. The notion is that a user must use something they have (first factor) and something they know (second factor) to authenticate. A password is something the user knows. For the “something they have” part, many things can be used. Some solutions use biometrics, like a fingerprint, voice pattern recognition, or iris scan. These are relatively expensive solutions. Other second factor authentication methods include one-time passwords (OTP). These are passwords that are generated on a device and good for use once. Typically there are two types of OTPs; counter based, and time based. Using 2FA is better than just username and password alone because it is very difficult for an attacker to procure both the password and the second factor.
In this tutorial, we will use Laravel and Google Authenticator to demonstrate how to implement 2FA in a webapp. Google Authenticator is just one implementation of the Time-Based One-Time Password (TOTP) algorithm, RFC 6238. This industry standard is used in a lot of various 2FA solutions. There are some advantages of Google Authenticator over some other 2FA solutions on the market. After you download the app to your smartphone, you can use it offline. Many other 2FA solutions need to be connected somehow; they send an SMS message, or a push notification, or even call the smartphone with a recorded message. This is not good for users that might be in a location where their phone is cut off from the outside world, like in an office located in the basement of a building.
How the TOTP works is that the server generates a secret key. This secret key is then passed to the user. The secret key is used in combination with the current Unix timestamp to generate a six digit number, using a keyed-hash message authentication code (HMAC) based algorithm. This six digit number is the OTP. It changes every 30 seconds.
This article assumes Laravel Homestead is installed. It is not necessary to use it, but the commands might differ slightly if you use a different environment and requires PHP 7. If you are not familiar with Homestead and want to produce similar results as this article aims to produce, please visit this SitePoint article that shows how to setup Homestead.
Create a new Laravel project.
Make sure you go to the project folder
There is a Laravel package that includes a PHP version of the Google Authenticator. We will use it in this project. First, we will include Antonio Carlos Ribeiro’s Laravel package using Composer. We will also install a library to do Base32 encoding in constant time.
After Composer has installed the package, we must now tell Laravel about it. Open config/app.php and add PragmaRXGoogle2FAVendorLaravelServiceProvider::class, to the providers array. Also, add 'Google2FA' => PragmaRXGoogle2FAVendorLaravelFacade::class, to the aliases array.
Laravel includes scaffolding to create all the controllers, views, and routes needed for basic user registration, login, etc. We will use the auth scaffolding to quickly build the login and registration screens.
We will modify some of the auto-generated code to add two-factor authentication.
We need to store the secret key used to create the one-time password in the user’s record. To do this, create a migration for the new database column needed.
Open the newly created migration file located in the database/migrations folder. It will be named something like 2016_01_06_152631_add_google2fa_secret_to_users.php. Replace the contents of the file with the code below:
When the migration is run, the column google2fa_secret is added to the users table. When the migration is rolled back, that column is removed from the table.
Next, we will run the migration to set up the database tables.
Now that the column google2fa_secret has been added to the users table, we should update the AppUser model to make it a bit more secure. By default, if the program converts the data from an instance of AppUser to JSON, the contents of the google2fa_secret column will be a part of the JSON object. We will block this action. Open the AppUser model, app/User.php, and add the array element google2fa_secret as a string to the hidden attribute.
Open up the web routes file routes/web.php and add the following routes to the bottom:
The routes we added do various things.
When a user goes to /2fa/enable, it will create the 2FA secret key and provide instructions on how to configure their device to use it. When a user goes to /2fa/disable, it will remove the 2FA secret key from the user’s account so that they can log in without having to provide the one-time password. After a user authenticates with their username and password, if they have a 2FA secret key set up, they will be redirected to /2fa/validate via a HTTP GET. This route shows a form where they can put in their one-time password. When the user submits the form, it goes to /2fa/validate via a HTTP POST. This route makes sure the one-time password passed in by the user is valid. The route also uses the throttle Laravel middleware. If an attacker has your password, it is easy to go through all combinations of the 6-digit token in an automated way. The throttle is set for a maximum of 5 requests per minute. Laravel’s throttle middleware is based on IP address. This article just shows you the concept of throttling and doesn’t protect against a distributed brute force attack. In a production implementation, consider doing throttling based on user.
We will create a new controller to enable and disable 2FA support for a user:
Open the new controller Google2FAController and replace the contents with the code below:
The method, enableTwoFactor(), gets executed when a user wants to setup 2FA. First, it generates a new secret key. Unfortunately at this time, the key generation function inside the 2FA library does not use a cryptographically secure PRNG, so we use the random_bytes() function to create the bytes for our secret key and Base32 encode it. Then, the method stores an encrypted version of the secret key to the users table.
Next, a 200x200px QR barcode image that contains the secret key and other metadata needed for the Google Authenticator mobile app is generated. This image is encoded in the Data URI scheme so we can inline it into the webpage. Next, the enableTwoFactor view is loaded to display the QR barcode and the secret key as text, just in case they use a RFC 6238 compatible app that can’t read QR barcodes.
The disableTwoFactor() method is called when a user wants to disable 2FA. It puts a null value into the database for the secret key and then displays a view that says that 2FA has been disabled.
Now, we will make a few modifications to the Auth controller. Open app/Http/Controllers/Auth/AuthController.php and put the following lines of code near the top in the use section:
After a user authenticates, the Auth controller sees if there is an authenticated() method. If so, the controller executes that method. By default, the scaffolding does not create the authenticated() method. We will create this method and use it to prompt someone to put in the one-time password if they have set up the 2FA secret key. Inside the same AuthController class, add the following method:
If the user has set up 2FA, this method logs out the user, saves their user id to the session, and redirects them to a page where they can enter the one-time password. If the user does not have 2FA set up, it redirects them to the homepage without logging them out.
The getValidateToken() method displays the page where a user can enter in their one-time password. In the AuthController class, add this method:
If the session with the user id does not exist, the user gets redirected to the login page. If the user id is set in the session, the view is shown.
Once the user clicks the “Validate” button on the one-time password page, the request is routed to the postValidateToken() method. Add the following code to the AuthController class:
Before this method is run, validation is done via the ValidateSecretRequest form request class. If validation fails, the postValidateToken() method is not executed. After validation, the one-time password is added to a blacklist for 4 minutes. This is done because the 2FA library allows codes up to 4 minutes old to be valid and we don’t want a user to be able to reuse a code during that window. Next, the user is logged into the system and redirected to the homepage.
Lastly, make sure that in the AuthController class, the redirectTo property is set to /home. This redirects users to the homepage /home instead of the welcome page / after login.
To create a new form request class, execute the following command:
Open app/Http/Requests/ValidateSecretRequest.php and replace the contents with the code below:
We are doing three things in this file;
In the class constructor, we set up the custom validator rules. The first rule makes sure that the user has presented a valid TOTP token. The second rule makes sure the user hasn’t submitted a token on the blacklist. Normally, custom validator rules are used for many different requests and so defined in the AppServiceProvider class. But, since these rules are only being used in one request, we put it in this class’ constructor.
The authorize() method checks and sees if it can get a user record based on the user id stored in the session. If this doesn’t work, then the request is not considered authorized.
The rules() method defines the validation rules. The rules say that the token must not be empty, be a six digit number, be valid, and not be on the user’s blacklist.
On the homepage, we need to add a section that allows a user to enable or disable 2FA. Open resources/views/home.blade.php and add the following code just before the last closing div tag:
When the user does not have the google2fa_secret field set, then a button is shown that allows them to enable 2FA. When the google2fa_secret field is set, then a button is shown that allows them to disable 2FA.
For this project, we will create some new views. We will put them in their own directory. Create the directory 2fa in the views folder:
Let’s create the page that displays the secret key with instructions on how to configure the user’s 2FA device. Create the view file enableTwoFactor.blade.php in the newly created resources/views/2fa folder and put the following contents into it:
If a user decides to disable 2FA, create a page to let them know it has been disabled. Create a file called disableTwoFactor.blade.php in the resources/views/2fa folder and put the following code into it:
After the user authenticates with their email and password, we need a page where a user can enter in their one-time password generated from their 2FA device. Create a new file called validate.blade.php in the resources/views/2fa folder and put the following view code into it:
We should now have all the pieces in place to make the example work.
You can use any RFC 6238 compatible program, but to follow along with this tutorial, please use Google Authenticator. First, go to the Apple iTunes App Store or the Google Play Store and download the app.
Next, open up the landing page of the app at You should see a welcome page. In the upper right hand corner, there is a sign-up link. Click on it and register for a new account.
Screenshot of landing page
Screenshot of registration page
Once you register for a new account, you should be automatically logged in and redirected to the homepage. On this page, you should see a section that talks about Two-Factor Authentication and enabling it. Click on the button to enable 2FA.
Screenshot of enable 2FA on homepage
On the screen, you should see a QR barcode. This is the secret key that the app generated that you need to load into your 2FA mobile app.
Screenshot of the enable 2FA page
Open up the Google Authenticator app and click on the add button at the bottom of the screen. It should ask if you want to scan a barcode or manually enter in the secret key. Pick the first option.
Screenshot of the Google Authenticator splash screen
Hold the phone up to the screen of your computer to scan the barcode. After the QR barcode is scanned, there should be a new entry in the list of one-time passwords.
Next, log out of the webapp, and re-login. This time, after you use your email and password, you should see a screen that asks for the one-time password.
Make sure the Google Authenticator app is open and put the number that is currently being displayed into the webpage and submit the value.
Screenshot of Google Authenticator home screen
Screenshot of the OTP page
After that, you should be authenticated and be directed to the homepage.
If you want to disable the 2FA, you can do it on the homepage by clicking on the “Disable 2FA” at the bottom.
Screenshot of the homepage
There will be a confirmation page saying that 2FA has been disabled.
Screenshot of the disabled 2FA page
Now you can log in without the one-time password.
By default, the login process and TOTP setup process does not happen via HTTPS. In a production environment, make sure it happens over HTTPS.
In this article, we have seen how adding a one-time password to the authentication process makes logging in more secure. Next, we walked through building an app in Laravel that uses an implementation of Google Authenticator to do 2FA.
If you would like to see the completed example, it is located on Github.
Chris is an App Development Manager for one of the top medical schools in Southern California. He has contributed to a few open source PHP projects.
© 2000 – 2022 SitePoint Pty. Ltd.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.


Related Post

Leave a Reply

Your email address will not be published.

Translate »