Laravel Tutorial - User Registration with Email Verification with gmail smtp in Laravel 10
1. Install Latest Laravel App
composer create-project --prefer-dist laravel/laravel CustomRegisterEmailConfirm
cd CustomRegisterEmailConfirm
2. Configure Database and SMTP Credentials
//.env file
Database Credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_db_name
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
SMTP Credentials:
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME="your_email@gmail.com"
MAIL_PASSWORD="your_password"
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="your_email@gmail.com"
MAIL_FROM_NAME="${APP_NAME}"
To work with MySQL database, you must need to have it in your system and PHP 8.1 is required for Laravel 10.
3. Implement MustVerifyEmail Contract in User Model
//Open app\Models\User.php and remove single line comment \\ from MustVerifyEmail contract to enable it.
use Illuminate\Contracts\Auth\MustVerifyEmail;
class User extends Authenticatable implements MustVerifyEmail
4. Migrate Tables to Database
php artisan migrate
5. Define Custom Registration, Verification, Home and Login Routes
//routes/web.php file.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\VerificationController;
use App\Http\Controllers\Auth\LoginRegisterController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
// Define Custom User Registration & Login Routes
Route::controller(LoginRegisterController::class)->group(function() {
Route::get('/register', 'register')->name('register');
Route::post('/store', 'store')->name('store');
Route::get('/login', 'login')->name('login');
Route::post('/authenticate', 'authenticate')->name('authenticate');
Route::get('/home', 'home')->name('home');
Route::post('/logout', 'logout')->name('logout');
});
// Define Custom Verification Routes
Route::controller(VerificationController::class)->group(function() {
Route::get('/email/verify', 'notice')->name('verification.notice');
Route::get('/email/verify/{id}/{hash}', 'verify')->name('verification.verify');
Route::post('/email/resend', 'resend')->name('verification.resend');
});
6. Create a LoginRegister Controller
php artisan make:controller Auth\LoginRegisterController
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Auth\Events\Registered;
class LoginRegisterController extends Controller
{
/**
* Instantiate a new LoginRegisterController instance.
*/
public function __construct()
{
$this->middleware('guest')->except([
'logout', 'home'
]);
$this->middleware('auth')->only('logout', 'home');
$this->middleware('verified')->only('home');
}
/**
* Display a registration form.
*
* @return \Illuminate\Http\Response
*/
public function register()
{
return view('auth.register');
}
/**
* Store a new user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:250',
'email' => 'required|string|email:rfc,dns|max:250|unique:users,email',
'password' => 'required|string|min:8|confirmed'
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password)
]);
event(new Registered($user));
$credentials = $request->only('email', 'password');
Auth::attempt($credentials);
$request->session()->regenerate();
return redirect()->route('verification.notice');
}
/**
* Display a login form.
*
* @return \Illuminate\Http\Response
*/
public function login()
{
return view('auth.login');
}
/**
* Authenticate the user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if(Auth::attempt($credentials))
{
$request->session()->regenerate();
return redirect()->route('home');
}
return back()->withErrors([
'email' => 'Your provided credentials do not match in our records.',
])->onlyInput('email');
}
/**
* Display a home to authenticated & verified users.
*
* @return \Illuminate\Http\Response
*/
public function home()
{
return view('auth.home');
}
/**
* Log out the user from application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('login')
->withSuccess('You have logged out successfully!');
}
}
7. Create a Verification Controller
php artisan make:controller Auth\VerificationController
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
class VerificationController extends Controller
{
/**
* Instantiate a new VerificationController instance.
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
/**
* Display an email verification notice.
*
* @return \Illuminate\Http\Response
*/
public function notice(Request $request)
{
return $request->user()->hasVerifiedEmail()
? redirect()->route('home') : view('auth.verify-email');
}
/**
* User's email verificaiton.
*
* @param \Illuminate\Http\EmailVerificationRequest $request
* @return \Illuminate\Http\Response
*/
public function verify(EmailVerificationRequest $request)
{
$request->fulfill();
return redirect()->route('home');
}
/**
* Resent verificaiton email to user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function resend(Request $request)
{
$request->user()->sendEmailVerificationNotification();
return back()
->withSuccess('A fresh verification link has been sent to your email address.');
}
}
middleware(‘throttle:6,1’) it simply means that user can make 6 resend verification email request within 1 minute of time.
8. Create Login, Register, and Verification Blade View Files
Now I will create an auth directory in resources/views/ directory and then create the following blade view files in it.
layouts.blade.php
register.blade.php
verify-email.blade.php
home.blade.php
login.blade.php
//resources/views/auth/layouts.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Laravel Tutorial - User Registration with Email Verification with gmail smtp in Laravel 10</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg bg-light">
<div class="container">
<a class="navbar-brand" href="{{ URL('/') }}">Custom Register with Email Verification</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav ms-auto">
@guest
<li class="nav-item">
<a class="nav-link {{ (request()->is('login')) ? 'active' : '' }}" href="{{ route('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link {{ (request()->is('register')) ? 'active' : '' }}" href="{{ route('register') }}">Register</a>
</li>
@else
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ Auth::user()->name }}
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();"
>Logout</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST">
@csrf
</form>
</li>
</ul>
</li>
@endguest
</ul>
</div>
</div>
</nav>
<div class="container">
@yield('content')
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>
</body>
</html>
//resources/views/auth/register.blade.php
@extends('auth.layouts')
@section('content')
<div class="row justify-content-center mt-5">
<div class="col-md-8">
<div class="card">
<div class="card-header">Register</div>
<div class="card-body">
<form action="{{ route('store') }}" method="post">
@csrf
<div class="mb-3 row">
<label for="name" class="col-md-4 col-form-label text-md-end text-start">Name</label>
<div class="col-md-6">
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ old('name') }}">
@if ($errors->has('name'))
<span class="text-danger">{{ $errors->first('name') }}</span>
@endif
</div>
</div>
<div class="mb-3 row">
<label for="email" class="col-md-4 col-form-label text-md-end text-start">Email Address</label>
<div class="col-md-6">
<input type="email" class="form-control @error('email') is-invalid @enderror" id="email" name="email" value="{{ old('email') }}">
@if ($errors->has('email'))
<span class="text-danger">{{ $errors->first('email') }}</span>
@endif
</div>
</div>
<div class="mb-3 row">
<label for="password" class="col-md-4 col-form-label text-md-end text-start">Password</label>
<div class="col-md-6">
<input type="password" class="form-control @error('password') is-invalid @enderror" id="password" name="password">
@if ($errors->has('password'))
<span class="text-danger">{{ $errors->first('password') }}</span>
@endif
</div>
</div>
<div class="mb-3 row">
<label for="password_confirmation" class="col-md-4 col-form-label text-md-end text-start">Confirm Password</label>
<div class="col-md-6">
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation">
</div>
</div>
<div class="mb-3 row">
<input type="submit" class="col-md-3 offset-md-5 btn btn-primary" value="Register">
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
//resources/views/auth/verify-email.blade.php
@extends('auth.layouts')
@section('content')
<div class="row justify-content-center mt-5">
<div class="col-md-8">
<div class="card">
<div class="card-header">Verify Your Email Address</div>
<div class="card-body">
@if ($message = Session::get('success'))
<div class="alert alert-success" role="alert">
{{ $message }}
</div>
@endif
Before proceeding, please check your email for a verification link. If you did not receive the email,
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">click here to request another</button>.
</form>
</div>
</div>
</div>
</div>
@endsection
//resources/views/auth/home.blade.php
@extends('auth.layouts')
@section('content')
<div class="row justify-content-center mt-5">
<div class="col-md-8">
<div class="card">
<div class="card-header">Home</div>
<div class="card-body">
<div class="alert alert-success">
You are logged in!
</div>
</div>
</div>
</div>
</div>
@endsection
//resources/views/auth/login.blade.php
@extends('auth.layouts')
@section('content')
<div class="row justify-content-center mt-5">
<div class="col-md-8">
<div class="card">
<div class="card-header">Login</div>
<div class="card-body">
@if ($message = Session::get('success'))
<div class="alert alert-danger text-center">
{{ $message }}
</div>
@endif
<form action="{{ route('authenticate') }}" method="post">
@csrf
<div class="mb-3 row">
<label for="email" class="col-md-4 col-form-label text-md-end text-start">Email Address</label>
<div class="col-md-6">
<input type="email" class="form-control @error('email') is-invalid @enderror" id="email" name="email" value="{{ old('email') }}">
@if ($errors->has('email'))
<span class="text-danger">{{ $errors->first('email') }}</span>
@endif
</div>
</div>
<div class="mb-3 row">
<label for="password" class="col-md-4 col-form-label text-md-end text-start">Password</label>
<div class="col-md-6">
<input type="password" class="form-control @error('password') is-invalid @enderror" id="password" name="password">
@if ($errors->has('password'))
<span class="text-danger">{{ $errors->first('password') }}</span>
@endif
</div>
</div>
<div class="mb-3 row">
<input type="submit" class="col-md-3 offset-md-5 btn btn-primary" value="Login">
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
9. Run Laravel Server
php artisan serve
Open your application on the browser.
http://127.0.0.1:8000/register