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
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}"
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
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',
];
}
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
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,
];
}
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');
});
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.');
}
}
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');
}
}
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>
Now, we will run laravel 9 two-factor authentication using email.
php artisan serve
You might also like:
- Read Also: Laravel 9 Two Factor Authentication With SMS
- Read Also: How To Send Email In Laravel 9 Using Mailgun
- Read Also: How To Send Email With Attachment In Laravel 9
- Read Also: Login with Mobile Number using Laravel Custom Auth