Simplifying Passwordless Login with Laravel

Getting Started

To get started, let’s create a new Laravel 8 application and set up our database credentials. We’ll also configure our mail driver to preview our login emails.

$ composer create-project --prefer-dist laravel/laravel project-name
$ php artisan migrate

The Technical Approach

To implement passwordless login, we need to:

  • Generate a unique token for each user
  • Associate it with their account
  • Validate it when they click the link

We’ll store these tokens in a separate table and keep track of whether they’ve been used or expired.

Creating the Login Flow

We’ll start by creating a test user and a login route that sends a unique link to their email address. When they click the link, we’ll validate the token and log them in.

// routes/web.php
Route::get('/login', 'LoginController@login');
Route::get('/login/{token}', 'LoginController@verify');

// app/Http/Controllers/LoginController.php
public function login()
{
    $user = User::first(); // Create a test user
    $token = $user->createToken();
    Mail::to($user->email)->send(new LoginEmail($user, $token));
    return 'Login link sent!';
}

public function verify($token)
{
    // Validate the token and log the user in
}

Implementing the sendLoginLink Function

Next, we’ll update our User model to generate a unique token and send it to the user via email. We’ll use Laravel’s built-in mailer class to compose the email and a temporary signed URL to ensure the link hasn’t been tampered with.

// app/Models/User.php
public function createToken()
{
    $token = Str::random(60);
    $this->tokens()->create(['token' => $token]);
    return $token;
}

// app/Mail/LoginEmail.php
public function build()
{
    return $this->markdown('emails.login')
        ->with([
            'url' => URL::temporarySignedRoute(
                'login.verify',
                now()->addMinutes(30),
                ['token' => $this->user->tokens()->latest()->first()->token]
            ),
        ]);
}

The Verification Route

When the user clicks the link, we’ll validate the token and log them in. We’ll also mark the token as used so it can’t be used again.

// app/Http/Controllers/LoginController.php
public function verify($token)
{
    $token = Token::where('token', $token)->first();
    if (!$token || $token->used) {
        return 'Invalid token';
    }
    $user = $token->user;
    Auth::login($user);
    $token->used = true;
    $token->save();
    return redirect()->intended('/');
}

Final Touches

Finally, we’ll guard our root route to only be viewable by logged-in users and add a way to log out. We’ll also update our welcome view to show information about the logged-in user and provide a link to log out.

// routes/web.php
Route::get('/', 'HomeController@index')->middleware('auth');

// resources/views/welcome.blade.php

Welcome, {{ Auth::user()->name }}!


Log out



// app/Http/Controllers/HomeController.php
public function index()
{
    return view('welcome');
}

With these steps, we’ve successfully implemented passwordless login using Laravel.

Leave a Reply