Secure Laravel Login with 2FA and Magic Link Authentication

In this tutorial, we’ll walk through creating a secure login system in Laravel that combines:
– Traditional login & registration
– Login via a unique magic link (a secure token)
– Two-Factor Authentication (2FA) for added security
– A dashboard showing user-specific content (e.g. reviews)

This is a great way to create a secure and user-friendly authentication flow for apps that need quick logins via email or external links while maintaining strong security.

Tools & Packages Used

– Laravel Breeze (auth scaffolding)
– Laravel Fortify (2FA)
– Laravel’s built-in Auth system

Step 1: Set Up Laravel & Breeze

Create a new Laravel project and install Breeze:

composer create-project laravel/laravel login-app
cd login-app

composer require laravel/breeze –dev
php artisan breeze:install
npm install && npm run dev
php artisan migrate

Step 2: Add Login Token Field

Generate a migration:

php artisan make:migration add_login_token_to_users_table

Edit the migration:

Schema::table(‘users’, function (Blueprint $table) {
$table->string(‘login_token’)->nullable()->unique();
});

Then run:

php artisan migrate

Step 3: Magic Link Login Route

In routes/web.php:

Route::get(‘/magic-login/{token}’, function ($token) {
$user = \App\Models\User::where(‘login_token’, $token)->firstOrFail();
\Illuminate\Support\Facades\Auth::login($user);
return redirect()->route(‘2fa.prompt’);
});

Step 4: Install Laravel Fortify for 2FA

Install Fortify:

composer require laravel/fortify
php artisan vendor:publish –provider=”Laravel\Fortify\FortifyServiceProvider”
php artisan migrate

In config/fortify.php:

‘features’ => [
Features::twoFactorAuthentication([
‘confirmPassword’ => false,
]),
]

Register in config/app.php:
App\Providers\FortifyServiceProvider::class,

Step 5: Add Dashboard and Review Model

Create a model and migration:

php artisan make:model Review -m

Update the migration:

Schema::create(‘reviews’, function (Blueprint $table) {
$table->id();
$table->foreignId(‘user_id’)->constrained()->onDelete(‘cascade’);
$table->string(‘content’);
$table->timestamps();
});

Then migrate:

php artisan migrate

Step 6: Create Dashboard Controller and View

Create controller:
php artisan make:controller DashboardController

In controller:

public function index() {
$reviews = \App\Models\Review::where(‘user_id’, auth()->id())->get();
return view(‘dashboard’, compact(‘reviews’));
}

In routes/web.php:

Route::middleware([‘auth’])->group(function () {
Route::get(‘/dashboard’, [DashboardController::class, ‘index’])->name(‘dashboard’);
});

Create Blade view in resources/views/dashboard.blade.php:

<h1>Welcome {{ auth()->user()->name }}</h1>
<h2>Your Reviews:</h2>
<ul>
@foreach($reviews as $review)
<li>{{ $review->content }}</li>
@endforeach
</ul>

Security Tips

– Token should be unique and long (64+ characters)
– You can add an expiration (e.g., login_token_expires_at)
– Optionally regenerate token after every login
– Log login attempts for auditing

Final Thoughts

Combining magic login links with two-factor authentication gives users a smooth experience while keeping your app secure. Laravel’s ecosystem makes this simple to implement using built-in packages like Breeze and Fortify.

This setup is ideal for:
– Client dashboards
– Internal tools
– Invitation-based platforms