Email verification ensures users own the email addresses they register with. Laravel makes this easy to implement.
Setting Up Email Verification
1. Implement the Contract
php<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; // Add this use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable implements MustVerifyEmail // Add this { use Notifiable; // ... }
2. Database Migration
The users table needs email_verified_at:
php$table->timestamp('email_verified_at')->nullable();
3. Define Routes
phpuse Illuminate\Foundation\Auth\EmailVerificationRequest; // Notice page Route::get('/email/verify', function () { return view('auth.verify-email'); })->middleware('auth')->name('verification.notice'); // Verification handler Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) { $request->fulfill(); // Marks email as verified return redirect('/dashboard'); })->middleware(['auth', 'signed'])->name('verification.verify'); // Resend verification Route::post('/email/verification-notification', function (Request $request) { $request->user()->sendEmailVerificationNotification(); return back()->with('message', 'Verification link sent!'); })->middleware(['auth', 'throttle:6,1'])->name('verification.send');
4. Protect Routes
php// Require email verification Route::get('/dashboard', [DashboardController::class, 'index']) ->middleware(['auth', 'verified']); // Or protect a group Route::middleware(['auth', 'verified'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); Route::get('/billing', [BillingController::class, 'index']); });
The Verification Notice View
blade<!-- resources/views/auth/verify-email.blade.php --> <x-layout> <div class="container"> <h1>Verify Your Email Address</h1> @if (session('message')) <div class="alert alert-success"> {{ session('message') }} </div> @endif <p>Before proceeding, please check your email for a verification link.</p> <p>If you did not receive the email:</p> <form method="POST" action="{{ route('verification.send') }}"> @csrf <button type="submit">Resend Verification Email</button> </form> </div> </x-layout>
Customizing the Verification Email
php<?php namespace App\Models; use Illuminate\Auth\Notifications\VerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable implements MustVerifyEmail { /** * Send the email verification notification. */ public function sendEmailVerificationNotification(): void { $this->notify(new VerifyEmail); // Or use your custom notification // $this->notify(new CustomVerifyEmail); } }
Custom Notification
php<?php namespace App\Notifications; use Illuminate\Auth\Notifications\VerifyEmail as BaseVerifyEmail; use Illuminate\Notifications\Messages\MailMessage; class CustomVerifyEmail extends BaseVerifyEmail { public function toMail($notifiable): MailMessage { $verificationUrl = $this->verificationUrl($notifiable); return (new MailMessage) ->subject('Welcome! Please Verify Your Email') ->greeting('Hello ' . $notifiable->name . '!') ->line('Thanks for signing up. Please verify your email address.') ->action('Verify Email', $verificationUrl) ->line('This link will expire in 60 minutes.') ->line('If you did not create an account, no action is needed.'); } }
Checking Verification Status
php// In a controller if ($request->user()->hasVerifiedEmail()) { // Email is verified } // In Blade @if (auth()->user()->hasVerifiedEmail()) <span class="badge">✓ Verified</span> @else <span class="badge">Pending verification</span> @endif
Events
Listen for verification events:
php// In EventServiceProvider protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], Verified::class => [ \App\Listeners\LogVerifiedUser::class, ], ];
php// app/Listeners/LogVerifiedUser.php class LogVerifiedUser { public function handle(Verified $event): void { Log::info('User verified email', [ 'user_id' => $event->user->id, 'email' => $event->user->email, ]); } }
Manual Verification
php// Mark as verified manually $user->markEmailAsVerified(); // Check verification $user->hasVerifiedEmail(); // true/false // Get verification timestamp $user->email_verified_at; // Carbon instance or null