Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Jamesking56's avatar

Multi-factor authentication

Hi,

Whats the best way to add email based multi-factor authentication to a Laravel app? I'm keen to get something working that is secure and correct.

I can see Laravel Fortify includes multi-factor code-based authentication but this is too technical for my end users, who are not tech savvy. So I am looking for something much more simple like an email link they click to trust a device.

Any ideas welcome.

Cheers.

0 likes
4 replies
LaryAI's avatar
Level 58

To implement email-based multi-factor authentication (MFA) in a Laravel application, you can follow these steps. This approach involves sending a unique token to the user's email, which they can click to verify their identity.

Step 1: Set Up Email Configuration

Ensure your Laravel application is configured to send emails. Update your .env file with the necessary email configuration:

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
[email protected]
MAIL_PASSWORD=your_email_password
MAIL_ENCRYPTION=tls
[email protected]
MAIL_FROM_NAME="${APP_NAME}"

Step 2: Create a Migration for MFA Tokens

Create a migration to store MFA tokens:

php artisan make:migration create_mfa_tokens_table

In the migration file, define the schema:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateMfaTokensTable extends Migration
{
    public function up()
    {
        Schema::create('mfa_tokens', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('token');
            $table->timestamp('expires_at');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('mfa_tokens');
    }
}

Run the migration:

php artisan migrate

Step 3: Generate and Send MFA Token

Create a method to generate and send the MFA token to the user's email:

use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use App\Models\MfaToken;
use App\Models\User;

public function sendMfaToken(User $user)
{
    $token = Str::random(40);
    $expiresAt = now()->addMinutes(10);

    MfaToken::create([
        'user_id' => $user->id,
        'token' => $token,
        'expires_at' => $expiresAt,
    ]);

    $verificationUrl = route('mfa.verify', ['token' => $token]);

    Mail::send('emails.mfa', ['url' => $verificationUrl], function ($message) use ($user) {
        $message->to($user->email);
        $message->subject('Your MFA Verification Link');
    });
}

Step 4: Create the Verification Route and Controller

Define a route for verifying the MFA token:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\MfaController;

Route::get('/mfa/verify/{token}', [MfaController::class, 'verify'])->name('mfa.verify');

Create the MfaController:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\MfaToken;
use Carbon\Carbon;

class MfaController extends Controller
{
    public function verify($token)
    {
        $mfaToken = MfaToken::where('token', $token)->first();

        if (!$mfaToken || $mfaToken->expires_at < now()) {
            return redirect('/login')->withErrors(['message' => 'Invalid or expired MFA token.']);
        }

        // Mark the device as trusted or log the user in
        // For example:
        auth()->loginUsingId($mfaToken->user_id);

        // Delete the token after successful verification
        $mfaToken->delete();

        return redirect('/home')->with('status', 'Device trusted successfully.');
    }
}

Step 5: Create the Email View

Create a Blade view for the MFA email (resources/views/emails/mfa.blade.php):

<!DOCTYPE html>
<html>
<head>
    <title>MFA Verification</title>
</head>
<body>
    <p>Click the link below to verify your device:</p>
    <p><a href="{{ $url }}">{{ $url }}</a></p>
</body>
</html>

Step 6: Integrate MFA in Your Authentication Flow

Modify your authentication flow to include the MFA step. For example, after a successful login, send the MFA token:

public function login(Request $request)
{
    // Validate the request...

    if (auth()->attempt($request->only('email', 'password'))) {
        $this->sendMfaToken(auth()->user());
        auth()->logout();

        return redirect('/login')->with('status', 'We have sent you an MFA verification link. Please check your email.');
    }

    return back()->withErrors(['email' => 'Invalid credentials.']);
}

This solution provides a simple and user-friendly way to implement email-based multi-factor authentication in a Laravel application.

jlrdw's avatar

Fortify includes multi-factor code-based authentication but this is too technical for my end users

The developer sets up everything, not users. It's seamless to the users.

Jamesking56's avatar

@jlrdw What I mean is, the end-users for this app don't know how to generate two factor authentication codes.

jlrdw's avatar

@Jamesking56

What I mean is, the end-users for this app don't know how to generate two factor authentication codes.

Who are you calling end-users? To me an end user is like me here on the laracasts forum.

If you are referring to a developer using the app (a download from Github), still you the developer has that already programmed in. But if a developer is using it, why wouldn't they understand?

As example One of my insurance companies uses two factor. I login in, a code is texted to my mobile phone, I enter the code (like125A78), then login is completed. All I had to do is enter a code.

Some two factor uses email instead.

Edit:

All is explained here: https://laravel.com/docs/11.x/fortify#authenticating-with-two-factor-authentication

Please or to participate in this conversation.