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 }}!
// app/Http/Controllers/HomeController.php
public function index()
{
return view('welcome');
}
With these steps, we’ve successfully implemented passwordless login using Laravel.