Laravel 9 Two Factor Authentication Using Email

    In this article, we will see laravel 9 two-factor authentication using email. Here, we will send the OTP code to email in laravel 8 and laravel 9. You can two-factor verification using email in laravel 9. Also, we will use google SMTP for sending emails.

    So, let's see two-factor authentication email in laravel 9, how to send OTP email using laravel 9, and how to send a verification code to email in laravel 9.

    Step 1: Install Laravel 9

    Step 2: Create Migration

    Step 3: Create Model 

    Step 4: Create Auth using Laravel Command

    Step 5: Create Middleware

    Step 6: Create Route

    Step 7: Create Controller

    Step 8: Create Mail Class

    Step 9: Create Blade File

    Step 10: Run Laravel Application

Step 1: Install Laravel 9

    In this step, we will install the laravel 9 application using the following command.

composer create-project laravel/laravel laravel_email_otp

    Also, we will configure the .env file.

MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
[email protected]
MAIL_PASSWORD=your_password
MAIL_ENCRYPTION=tls
[email protected]
MAIL_FROM_NAME="${APP_NAME}"
Read Also:How To Add Digital Signature In PDF In Laravel 9
Step 2: Create Migration

    In this step, we will create a migration using the following command.

php artisan make:migration create_user_email_codes

    Migrations:

<?php
  
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
  
class CreateUserEmailCodes extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_email_codes', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id');
            $table->string('code');
            $table->timestamps();
        });
    }
  
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_email_codes');
    }
}

    After that, we will migrate the table into the database using the following command.

php artisan migrate
Step 3: Create Model

    Now, we will update the User model and create a new UserEmailCode model.

    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 Laravel\Sanctum\HasApiTokens;
use Exception;
use Mail;
use App\Mail\SendEmailCode;
  
class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
  
    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */
    protected $fillable = [
        'name',
        'email',
        'password'
    ];
  
    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
  
    /**
     * The attributes that should be cast.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
  
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function generateCode()
    {
        $code = rand(1000, 9999);
  
        UserEmailCode::updateOrCreate(
            [ 'user_id' => auth()->user()->id ],
            [ 'code' => $code ]
        );
    
        try {
  
            $details = [
                'title' => 'Mail Sent from Websolutionstuff',
                'code' => $code
            ];
             
            Mail::to(auth()->user()->email)->send(new SendEmailCode($details));
    
        } catch (Exception $e) {
            info("Error: ". $e->getMessage());
        }
    }
}

    app/Models/UserEmailCode.php

<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
  
class UserEmailCode extends Model
{
    use HasFactory;
  
    public $table = "user_email_codes";
  
    protected $fillable = [
        'user_id',
        'code',
    ];
}
Read Also:Laravel 9 Authentication Using Inertia JS
Step 4: Create Auth using Laravel Command

    In this step, we will create a laravel auth scaffold using the laravel composer command.

composer require laravel/ui

    After that, we will install bootstrap auth using the following command.

php artisan ui bootstrap --auth
Step 5: Create Middleware

    In this step, we will create middleware for verifying. So, run the following command and create middleware.

php artisan make:middleware Check2FA

    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);
    }
}

    app/Http/Kernel.php

<?php
  
namespace App\Http;
 
use Illuminate\Foundation\Http\Kernel as HttpKernel;
  
class Kernel extends HttpKernel
{
    ...
    ...
    ...
  
    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 6: Create Route

    Now, we will add routes in the web.php file.

    routes/web.php

<?php
  
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
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', [HomeController::class, 'index'])->name('home')->middleware('2fa');
  
Route::controller(TwoFAController::class)->group(function(){
    Route::get('two-factor-authentication', 'index')->name('2fa.index');
    Route::post('two-factor-authentication/store', 'store')->name('2fa.store');
    Route::get('two-factor-authentication/resend', 'resend')->name('2fa.resend');
});
Read Also:Laravel 9 Multiple Authentication Using Middleware
Step 7: Create Controller

    In this step, we will update LoginController and create a new TwoFAController.

    app/Http/Controllers/Auth/LoginController.php

<?php
   
namespace App\Http\Controllers\Auth;
  
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Auth;
use App\Models\UserEmailCode;
  
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 code on Method
     *
     * @return response()
     */
    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required',
            'password' => 'required',
        ]);
     
        $credentials = $request->only('email', 'password');
        if (Auth::attempt($credentials)) {
  
            auth()->user()->generateCode();
  
            return redirect()->route('2fa.index');
        }
    
        return redirect("login")->withSuccess('Oppes! You have entered invalid credentials);
    }
}

    app/Http/Controllers/TwoFAController.php

<?php
  
namespace App\Http\Controllers;
  
use Illuminate\Http\Request;
use Session;
use App\Models\UserEmailCode;
  
class TwoFAController extends Controller
{
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function index()
    {
        return view('2fa');
    }
  
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function store(Request $request)
    {
        $request->validate([
            'code'=>'required',
        ]);
  
        $find = UserEmailCode::where('user_id', auth()->user()->id)
                        ->where('code', $request->code)
                        ->where('updated_at', '>=', now()->subMinutes(1))
                        ->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 code on Method
     *
     * @return response()
     */
    public function resend()
    {
        auth()->user()->generateCode();
  
        return back()->with('success', 'We re-sent to code on your email.');
    }
}
Step 8: Create Mail Class

    Now, we will create a mail class for sending mail to users. So, run the following command.

php artisan make:mail SendEmailCode

    app/Mail/SendEmailCode.php

<?php
  
namespace App\Mail;
  
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
  
class SendEmailCode 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 Websolutionstuff')
                    ->view('emails.code');
    }
}
Step 9: Create Blade File

    In this step, we will create a 2fa.blade.php file.

    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">Laravel 9 Two Factor Authentication Using Email - Websolutionstuff</div>
  
                <div class="card-body">
                    <form method="POST" action="{{ route('2fa.store') }}">
                        @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

    resources/views/emails/code.blade.php

<!DOCTYPE html>
<html>
<head>
    <title>Websolutionstuff</title>
</head>
<body>
    <h1>{{ $details['title'] }}</h1>
    <p>Your code is: {{ $details['code'] }}</p>
     
    <p>Thank you</p>
</body>
</html>
Read Also:Laravel 9 REST API With Passport Authentication
Step 10: Run Laravel Application

    Now, we will run laravel 9 two-factor authentication using email.

php artisan serve

    You might also like:

Bình luận
Vui lòng đăng nhập để bình luận
Một số bài viết liên quan