Laravel 8 Two Factor Authentication using Email Example

Oct 26, 2021 . Admin



Hi Dev,

In this Article I will share something new about how actually work two-factor authentication integrate with laravel 8. I will create two-factor authentication using email in laravel 8.

So, In this article, i will give you step by step all information on how to add two-factor authentication with sending mail in laravel. we will use google Gmail SMTP for sending mail with code. you can follow bellow steps and make it done.

Here, I will give you a simple and easy example of how to use and implement two-factor authentication using email in laravel 8 in laravel simply follow my all steps.

Verify Code Page:



Email Content:



Step 1 : Install Laravel App

In this step, You can install laravel fresh app. So open the terminal and put the bellow command.

composer create-project --prefer-dist laravel/twoFA
Step 2 : Setup Database Configuration

After successfully install laravel app next we configure databse setup. We will open ".env" file and change the database name, username and password in the env file.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=Enter_Your_Database_Name
DB_USERNAME=Enter_Your_Database_Username
DB_PASSWORD=Enter_Your_Database_Password
Step 3 : Create Table Migration and Model
php artisan make:migration create_user_codes

In this step we have to create migration for user_codes table.

Path : /database/migrations/2021_10_23_042831_create_user_codes.php
id();
            $table->integer('user_id');
            $table->string('code');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_codes');
    }
}

Now we need to run migration be bellow command:

php artisan migrate
Step 4: Create and Update Models

After you have to put bellow code in your User model file to create users table.

Path : app/Models/User.php
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Exception;
use Mail;
use App\Mail\SendCodeMail;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function generateCode()
    {
        $code = rand(1000, 9999);

        UserCode::updateOrCreate(
            [ 'user_id' => auth()->user()->id ],
            [ 'code' => $code ]
        );

        try {
            
            $details = [
                'title' => 'Mail from MyWebtuts.com',
                'code' => $code
            ];

            Mail::to(auth()->user()->email)->send(new SendCodeMail($details));
        } catch (Exception $e) {
            info("Error: ". $e->getMessage());
        }
    }
}
Path : app/Models/UserCode.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class UserCode extends Model
{
    use HasFactory;

    public $table = "user_codes";
    
    protected $fillable = [
        'user_id',
        'code',
    ];
}

Step 5 : Create Auth using scaffold

Now, in this step, we will create auth scaffold command to create login, register and dashboard. so run following commands:

composer require laravel/ui

After, that you can run following command and check ui commands info.

php artisan ui --help

Next, You can use the following commands for creating auth:

Using Bootstrap:

php artisan ui bootstrap --auth
npm install
npm run dev
Step 6: Create Check2FA Middleware

In this step we will needed to create a Check2FA Middleware for users is 2fa or not thats to route.so follow bellow code.

php artisan make:middleware Check2FA
Path : app/Http/middleware/Check2FA.php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Session;

class Check2FA
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        if (!Session::has('user_2fa')) {
            return redirect()->route('2fa.index');
        }
        return $next($request);
    }
}

Path : app/Http/Kernel.php
/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    '2fa' => \App\Http\Middleware\Check2FA::class,
];
Step 7 : Create Route

now, we require to create a route for TwoFAController in laravel application. so open your "routes/web.php" file and add the following route.

Path : /routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TwoFAController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home')->middleware('2fa');

Route::get('2fa', [TwoFAController::class, 'index'])->name('2fa.index');
Route::post('2fa', [TwoFAController::class, 'store'])->name('2fa.post');
Route::get('2fa/reset', [TwoFAController::class, 'resend'])->name('2fa.resend');
Step 8 : Create and Update Controllers Path : app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Auth;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }
    
    /**
     * Write Your Code..
     *
     * @return string
    */
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required',
            'password' => 'required'
        ]);

        $cred = $request->only('email','password');
        if (Auth::attempt($cred)) {

            auth()->user()->generateCode();

            return redirect()->route('2fa.index');

        }

        return redirect("login")->withSuccess('Oppes! You have entered invalid credentials');
    }
}

Here, in this step now we should create a new controller as TwoFAController. So run the below command and create a new controller.

php artisan make:controller TwoFAController
After successfully run the above command. TwoFAController So, let's copy bellow code and put it on TwoFAController.php file. Path : app/http/controller/TwoFAController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Session;
use App\Models\UserCode;

class TwoFAController extends Controller
{
    /**
     * Write Your Code..
     *
     * @return string
    */
    public function index()
    {
        return view('2fa');
    }

    /**
     * Write Your Code..
     *
     * @return string
    */
    public function store(Request $request)
    {
        $request->validate([
            'code'=>'required',
        ]);

        $find = UserCode::where('user_id',auth()->user()->id)
                        ->where('code',$request->code)
                        ->where('updated_at','>=',now()->subMinutes(2))
                        ->first();

        if (!is_null($find)) {
            Session::put('user_2fa',auth()->user()->id);
            return redirect()->route('home');
        }

        return back()->with('error', 'You entered wrong code.');
    }

    /**
     * Write Your Code..
     *
     * @return string
    */
    public function resend()
    {
        auth()->user()->generateCode();
  
        return back()->with('success', 'We sent you code on your email.');
    }
}

Step 9 : Create Mail Class

Here, in this step we will need to create mail class and need to add configuration on email file. so let's run following command and update file.

php artisan make:mail SendCodeMail
Path : app/Mail/SendCodeMail.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class SendCodeMail extends Mailable
{
    use Queueable, SerializesModels;

    public $details;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($details)
    {
        $this->details = $details;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->subject('Mail From MyWebtuts.com')->view('emails.code');
    }
}

Path : .env
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=mygoogle@gmail.com
MAIL_PASSWORD=rrnnucvnqlbsl
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=mygoogle@gmail.com
MAIL_FROM_NAME="${APP_NAME}"
Step 10 : Create Blade File

In this step we have to engender a 2fa.blade.php. So finally you have to create the following file and put bellow code:

Path : /resources/views/2fa.blade.php
@extends('layouts.app')
  
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">2FA Verification</div>
  
                <div class="card-body">
                    <form method="POST" action="{{ route('2fa.post') }}">
                        @csrf
  
                        <p class="text-center">We sent code to email : {{ substr(auth()->user()->email, 0, 5) . '******' . substr(auth()->user()->email,  -2) }}</p>
  
                        @if ($message = Session::get('success'))
                            <div class="row">
                              <div class="col-md-12">
                                  <div class="alert alert-success alert-block">
                                    <button type="button" class="close" data-dismiss="alert">×</button> 
                                      <strong>{{ $message }}</strong>
                                  </div>
                              </div>
                            </div>
                        @endif
  
                        @if ($message = Session::get('error'))
                            <div class="row">
                              <div class="col-md-12">
                                  <div class="alert alert-danger alert-block">
                                    <button type="button" class="close" data-dismiss="alert">×</button> 
                                      <strong>{{ $message }}</strong>
                                  </div>
                              </div>
                            </div>
                        @endif
  
                        <div class="form-group row">
                            <label for="code" class="col-md-4 col-form-label text-md-right">Code</label>
  
                            <div class="col-md-6">
                                <input id="code" type="number" class="form-control @error('code') is-invalid @enderror" name="code" value="{{ old('code') }}" required autocomplete="code" autofocus>
  
                                @error('code')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
  
                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <a class="btn btn-link" href="{{ route('2fa.resend') }}">Resend Code?</a>
                            </div>
                        </div>
  
                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    Submit
                                </button>
  
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Path : resources/views/emails/code.blade.php
<!DOCTYPE html>
<html>
<head>
    <title>MyWebtuts.com</title>
</head>
<body>
    <h1>{{ $details['title'] }}</h1>
    <p>Your code is : {{ $details['code'] }}</p>
     
    <p>Thank you</p>
</body>
</html>

Now we are ready to run our example so run bellow command for quick run:

php artisan serve
localhost:8000/

It will help you...

#Laravel 8 #Laravel