These days, security is very important. That's why many applications implement two-factor authentication. In this tutorial, I will show you how to do that in Laravel, using Laravel Notifications and sending a one-time password via email or SMS.
Notice: there are more complicated 2FA methods like Google Authenticator, but in this tutorial I prefer the most simple and most widely used approach of email/SMS.
Prepare Laravel Application Back-End
For a quick authentication scaffold, we will use Laravel Breeze. Install it by running these two commands:
composer require laravel/breeze --devphp artisan breeze:install
Next, we need to store our verification code somewhere. Also, we need to set its expiration time, so there's another DB field for this. So, add two fields to the default users migration:
database/migrations/2014_10_12_000000_create_users_table.php:
public function up(){ Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->string('two_factor_code')->nullable(); // [tl! add] $table->dateTime('two_factor_expires_at')->nullable(); // [tl! add] $table->timestamps(); });}
We also add those fields to app/Models/User.php properties $fillable array:
class User extends Authenticatable{ protected $fillable = [ 'name', 'email', 'password', 'two_factor_code', // [tl! add] 'two_factor_expires_at', // [tl! add] ]; // ...
Finally, for filling those fields let's create a method in the...
Premium Members Only
This advanced tutorial is available exclusively to Laravel Daily Premium members.
Already a member? Login here
Premium membership includes:
Comments & Discussion
Hi mr. Povilas.
Thank you for your psots and cources.
I'm using 2FA but instead of storing the OTP and its expiration date in the database, I create cach value and life time of it and when user login the value will forgit as:
Cache::put($user->id . '-otp', $otp, now()->addMinutes(5));
is that correct? is it better to store it in the database?.
thank you in advance.
Hi Povilas,
Thank you for your helpful post on two-factor authentication. I have a question: after I verify my code and am redirected to the dashboard, if I manually hit the verify route again, the two-factor authentication code page appears again even though I have already verified.
Thanks
Hello Povilas, I like the simplicity of this approach, but... In most systems, users are prompted with an option to select between various authentications forms, SMS, Email or Google authenticator for example, which can be done by simple tweaks in the system, but from what I see in the code (I haven't tested it) is that the middleware can cause issues and some other things are not handled, for instance:
If user 1 is logged in and they went through the code authentication step, and at the same time, some other user, using the same credentials, the first user will be prompted to enter verification code, basically this approach prohibits 2 users from using the same account (many cases multiple users in a company can use the same account to log in), I think in this case it's better to use a session to store the codes no? Also another thing, to save on SMS cost specifically, instead of prompting the users for code everytime they login, the browser and IP should be saved somewhere in the DB, if a user logs in from the same browser, on the same machine in the same location, they should be able to proceed without the verification step cause it becomes an overkill. What do you think?
Hi (sorry, not Povilas here :) )
When you add a 2fa - in theory you must log everyone out on that account. This is a security practice to follow. Just imagine, you have a bad guy logged into your account - and you add 2FA. This didn't kill the session, so he can still do whatever he wants inside...
Same thing with SMS - that's for people to decide how they trust the proccess. For example, if you are working in a sensitive field - each login might require 2FA. But if that's a blog - then definitely you need to remember the browser.
But again, as always - these things depend... So can't give you a "this is the way to do it" type of an answer, sorry
Thanks for the reply Modestas! I somewhat agree with what you say, but as you mentioned, there is no right way to do something, it all "depends" Big enterprises like Airlines for example, they give you access to their portal, but they give you one single account to be used by multiple people, when you enable 2FA on that account, whoever tries to log in, they'll need to ask you for the OTP, which makes sense, but storing the OTP in the database, will prevent this from happening. Regarding logging out users, I don't necessarily agree with that, a use can't log in unless you willingly give them an OTP to use, if for no matter what reason, you gave the OTP to the wrong person and they gained access, you have the option to log everyone out of the account, I believe JetStream works this way.
But that's my point - in some cases you might want to store the 2FA information (browser, IP, mac, whatever) to remember that user has confirmed it. In other cases, you do need that additional security level to simply ask for 2FA at all logins. And usually it depends on the IT security practices (for example, if you want to be in compliance with some sort of audit).
As for the logging out - I was talking more about the case, where you did not have 2FA and added it. That point should log out all other sessions. But if you have 2FA - then yeah, it makes sense to keep the sessions active, as the logged in browsers "should" be trusted.
In any case, this is very "it depends" and I've seen them done both ways. I even did it both ways myself, as I had to comply with certification proccess. But which is best - I'm not sure. You can probably skip the 2FA requirement as long as you are storing the IP or something and once new IP is used - you ask for 2FA again. That way, in theory, you limit hacking posibility :)
Hi Povilas.
First, quality stuff, as always. It worked at first try.
Comparing this approach with the Jetstream 2FA, wich uses some authenticator app, like google or microsoft, witch approach do you recommend for a company webapp?
Jetstream works with external 2FA providers, which is more reliable, as some people consider email/SMS as not-true 2FA.
So if you care more about "technically correct" security, probably Jetstream way is more recommended, but also much more complex to implement and require your users to install authenticator apps.