Laravel — Dual(2 factor) authentication using email
In this article, we talk about how to implement dual or 2-factor authentication using username and password followed by email. This is purely for programmers. First, the user enters his/her email address and password, after validating that, a token is generated which then gets sent to his email address. The user can login only by using this token in the next step.
Laravel ships with a default authentication method for login. But here we cannot depend on the default authentication method. Let’s look at it in more detail.
First, we are going to define the action from login in routes.
Route::post('login', '[email protected]');
When the submit button from the login screen is clicked, the action goes to the authenticate function in LoginController which we are going to define.
Run the following command to create the LoginController.
php artisan make:controller LoginController
Now we are going to define our function
public function authenticate(Request $request) { $this->validate($request, [ 'email' => 'required|email', 'password' => 'required', ]); $email = $request->email; $password = $request->password; $auth = User::authenticate($email)->first(); if (!empty($auth) && Hash::check($password, $auth->password)) { $auth->update(['email_token' => bin2hex(openssl_random_pseudo_bytes(4))]); Notification::send($auth, new AuthenticateUser($auth)); return view('auth.login-verify'); } return redirect(route('login')) ->withInput($request->only('email', 'remember')) ->withErrors([ 'password' => 'Invalid Credentials', ]); }
The function first validates for proper email and password fields.
Then you can find the following line.
$auth = User::authenticate($email)->first();
At the top of the page, you should call the user model
use App\User;
The authenticate is a function that should be defined in the User model
public function scopeAuthenticate($query, $email) { return $query->where('email', '=', $email); }
The function checks whether the email address exists in the user table
Now going to the authenticate function in our LoginController, the $auth variable now will have the user details.
Now we can check the following line.
if (!empty($auth) && Hash::check($password, $auth->password))
Here it checks if the $auth is not empty and the password matches the one for the user.
You should call the Hash facade on top of the page.
use Hash; $auth->update(['email_token' => bin2hex(openssl_random_pseudo_bytes(4))]);
You should add the email_token field to the user migration.
Here we update the field email_token in our users’ table with a random token generated using the bin2hex(openssl_random_pseudo_bytes(4)) function.
openssl_random_pseudo_bytes($length) function generates a string of pseudo-random bytes, with the number of bytes determined by the length parameter.
The bin2hex($string) function converts a string of ASCII characters to hexadecimal values.
The above function generates an 8 digit code.
Now let’s send this code to the user’s email using
Notification::send($auth, new AuthenticateUser($auth));
You should call
use App\Notifications\AuthenticateUser;
on top of the page.
For this, to work we should create an AuthenticateUser class for notification
Run the following command to create this class
php artisan make:notification AuthenticateUser
Now we should have a class AuthenticateUser with some functions already created inside it.
Inside the function toMail($notifiable) which will be already there
Update to the following
return (new MailMessage) ->line('Here is your email token') ->line('To login, enter the code in the web page where you requested it.') ->line($notifiable->email_token') ;
You can customize the above to generate the email in the way you want. Now coming to our LoginController authenticate function, after sending the email token the function will return to a view to enter the email token.
return view('auth.login-verify');
You can have a text box and a submit button to a form in the view, with action to the following route which we should define in routes file.
Route::post('/verify', '[email protected]')->name('email-verify');
Now let’s define the emailVerify function
public function emailVerify() { $token = request('email_token'); if (!$token) { return redirect('login')->withErrors(['email' => 'Email Verification Token not provided!']); } $user = User::where('email_token', $token)->first(); if (!$user) { return redirect('login')->withErrors(['flash-error' => 'Invalid Email Verification Token!']); } Auth::login($user); return redirect('home'); }
In this function, we check whether the email_token is valid and on success, login using the Auth::login($user) function.
You should call ‘use Auth;’ facade on top of the page.
You have now successfully created dual authentication using email.
As a Laravel Application Development Company, we adopt these ways to get it done easily.
Originally published at 2hatslogic.com on May 23, 2018.