Over 05 years we help companies reach their financial and branding goals. webtechguru is a values-driven technology agency dedicated.

Gallery

Contacts

support@webtechguru.in

How to add custom email verification in laravel 12

How to Add Custom Email Verification in Laravel 12

In this guide, you’ll learn how to implement custom email verification in Laravel 12 using a simple mailer system. This approach will allow you to have full control over the email verification logic, style, and flow, instead of relying on Laravel’s default implementation.

Verification Workflow Overview

  1. Configure email settings in .env
  2. Generate App Password (for Gmail)
  3. Create custom mail class using php artisan make:mail
  4. Encrypt and send a verification link
  5. Decrypt email from the verification link
  6. Mark user as verified
  7. Display custom email verification template

Step 1: Email Config in .env

Update your .env file with your SMTP credentials:

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=example@gmail.com
MAIL_PASSWORD=mhzodjvkwleqns
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=example@gmail.com
MAIL_FROM_NAME="My App"

⚠️ Note: If you’re using Gmail, make sure you’ve created an App Password under your Google Account security settings.

Step 2: Create the Mail Class

Run the following artisan command:

php artisan make:mail VerifyUser

App\Mail\VerifyUser.php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class VerifyUser extends Mailable
{
    use Queueable, SerializesModels;

    public $link;

    public function __construct($link)
    {
        $this->link = $link;
    }

    public function build()
    {
        return $this->view('mail.user-verify')->with([
            'link' => $this->link,
        ]);
    }
}

Step 3: Create the Verification Email View

php artisan make:view mail.user-verify

resources/views/mail/user-verify.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Email Verification</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <style>
        body { font-family: Arial, sans-serif; background: #f8f9fa; margin: 0; padding: 0; }
        .container { background: #fff; max-width: 600px; margin: 30px auto; padding: 30px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); }
        .header { font-size: 22px; font-weight: bold; color: #343a40; }
        .message { font-size: 16px; color: #495057; margin: 20px 0; }
        .button { background-color: #0d6efd; padding: 12px 24px; color: white; text-decoration: none; border-radius: 5px; }
        .footer { font-size: 13px; color: #6c757d; text-align: center; margin-top: 25px; }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">Verify Your Email Address</div>
        <div class="message">
            <p>Hello,</p>
            <p>Thank you for registering. Please verify your email address by clicking the button below.</p>
            <p><strong>This link will expire in 10 minutes.</strong></p>
        </div>
        <div style="text-align: center;">
            <a href="{{ $link }}" class="button">Verify Email</a>
        </div>
        <div class="message">
            If the button doesn't work, copy and paste this link:
            <p><a href="{{ $link }}">{{ $link }}</a></p>
        </div>
        <div class="footer">If you did not request this email, you can ignore it.</div>
    </div>
</body>
</html>

Step 4: Create the Route

In your routes/web.php:

Route::get('/account/verify-user/{email}', [LoginController::class, 'VerifyUser'])->name('account.verify-user');

Step 5: Update Your Registration Logic

In your LoginController.php or RegisterController.php, add the logic:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use App\Models\User;
use App\Mail\VerifyUser;

public function processRegister(Request $request)
{
    $validator = Validator::make($request->all(), [
        'name' => 'required',
        'email' => 'required|email|unique:users',
        'password' => 'required|confirmed|min:6',
    ]);

    if ($validator->fails()) {
        return redirect()->route('account.register')->withErrors($validator)->withInput();
    }

    $user = new User();
    $user->name = $request->name;
    $user->username = $request->name;
    $user->email = $request->email;
    $user->password = Hash::make($request->password);
    $user->role = 'user';
    $user->save();

    $encryptedEmail = Crypt::encryptString($request->email);
    $verificationLink = url('/account/verify-user/' . $encryptedEmail);

    try {
        Mail::to($request->email)->send(new VerifyUser($verificationLink));
        return redirect()->route('account.login')->with('success', 'You have successfully registered. Please check your email.');
    } catch (\Exception $e) {
        \Log::error("Email sending failed: " . $e->getMessage());
        return redirect()->back()->with('error', 'Could not send verification email.');
    }
}

Step 6: Email Verification Logic

In your LoginController.php:

use Illuminate\Support\Carbon;

public function VerifyUser($email)
{
    try {
        $orgEmail = Crypt::decryptString($email);
        $user = User::where('email', $orgEmail)->first();

        if ($user) {
            $user->email_verified_at = Carbon::now();
            $user->save();

            return redirect()->route('account.login')->with('success', 'Email verified successfully.');
        }

        return redirect()->route('account.login')->with('error', 'User not found.');
    } catch (\Exception $e) {
        return redirect()->route('account.login')->with('error', 'Invalid or expired verification link.');
    }
}

Final Thoughts

Now you’ve successfully implemented custom email verification in Laravel 12!

This gives you:

  • Full control over email design
  • Better error handling
  • Custom expiration, styling, and behavior
  • Compatibility with any SMTP server (like Gmail, Mailgun, etc.)

Bonus Tip

Use signed routes with expiration using Laravel’s URL::temporarySignedRoute to improve security.

Need that too? I can help you add it!

Author

Admin

Leave a comment

Your email address will not be published. Required fields are marked *