Hello developer! In this guide, we'll learn about laravel 10 two-factor authentication using email. Here, we'll see how to two-factor authentication using email in laravel 10. we'll send the OTP code to the email in laravel 10.
For the two-factor authentication in laravel 10, we'll send OTP over the email and then we'll verify the OTP. So, we'll create migration, modal, and middleware.
To install the Laravel 10 application, use the following command.
composer create-project laravel/laravel laravel_10_two_factor_auth
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}"
To create a migration, use 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 creating the migration, you can migrate the table into the database by running the following command:
php artisan migrate
Let's 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;
use App\Models\UserEmailCode;
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 Vidvatek',
'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',
];
}
Next, we'll create a Laravel auth scaffold using the Laravel Composer command.
composer require laravel/ui
After creating the Laravel auth scaffold, we'll install Bootstrap auth using the following command.
php artisan ui bootstrap --auth
To create middleware for verification, execute the following command.
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, let's add routes in the web.php file. Copy and paste the following code into that 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');
});
Next, we'll update the LoginController and create a new TwoFAController using the following command.
php artisan make:controller 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 code to on your email.');
}
}
To create a mailable class for sending mail to users, execute 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 Vidvatek')
->view('emails.code');
}
}
Let's create a file named 2fa.blade.php and add the following HTML code to it.
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 10 Two Factor Authentication Using Email - Vidvatek</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>
To run Laravel 10 two-factor authentication using email, execute the necessary commands and configurations as per your setup
php artisan serve
You might also like:
- Read Also: Laravel 10 REST API Authentication using Sanctum
- Read Also: How to Create CRUD with Image Upload in Laravel 10
- Read Also: How to Send Bulk Mail using Queue in Laravel 10
- Read Also: Laravel 10 With Vue JS CRUD Operation