DEV Community

loading...
Cover image for Create (passwordless) Login Links for users in Laravel

Create (passwordless) Login Links for users in Laravel

koost89
・4 min read

As I was looking for a functionality that lets authenticatables log in in my Laravel applications with a simple click of a link, I came across a few packages already developed for Laravel, these packages did not what I specifically needed (namely give me full control over the expiration time and at the same time the amount of requests the URL would accept, a command as well as a way to generate them in my application’s codebase and have tested support for all current PHP and Laravel versions). Therefore I set out to create my own package, named Login Links for Laravel. In this post I will show you step by step how to use this in your own Laravel 6, 7 or 8 application.

Lets go!

Now I am not going to show you how to create a new Laravel application as there are tons of resources for that online.

We start by requiring this package in our existing application.

composer require koost89/laravel-login-links
Enter fullscreen mode Exit fullscreen mode

Once composer is happy with our decision to install the package, we can go ahead and publish the vendor files that come with Login Links for Laravel.
First off: the migration, this is publishable by running the following command.

php artisan vendor:publish --provider="Koost89\LoginLinks\LoginLinkServiceProvider" --tag="login-links-migrations"
Enter fullscreen mode Exit fullscreen mode

This command will publish the migrations needed for the package to operate. To give a bit of information as to why we need a table for the login links, its fairly simple. We need a way to store the URL, so we can monitor the amount of visits the URL has gotten (and dispose of it once it reaches it’s maximum amount of allowed clicks).

Next up we need to actually execute the migration, this is done by running

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

This will create the login_link_tokens table which we use to store the URL’s.

After this we can (and you probably would want to) publish the config file

php artisan vendor:publish --provider="Koost89\LoginLinks\LoginLinkServiceProvider" --tag="login-links-config"
Enter fullscreen mode Exit fullscreen mode

The config file that was published in config/login-links.php contains a few niceties for you to configure, I’d suggest you take a look at them and edit them however you seem fit, but the default config should work just fine with one more step.

In your App\Models\User class or any other class that extends the Authenticatable contract you can add the trait: Koost89\LoginLinks\Traits\CanLoginWithLink;

Your user (or other authenticatable) should look something like this:

use Koost89\LoginLinks\Traits\CanLoginWithLink;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use CanLoginWithLink;    // the rest of your class
}
Enter fullscreen mode Exit fullscreen mode

After this, you should be all set to start generating login links! Easy right?

So lets start creating some links!

$user = User::first();
$url = $user->generateLoginLink(); 
Enter fullscreen mode Exit fullscreen mode

which should output the following

http://yourdomain.tld/uli?auth_id=1&auth_type=QXBwX01vZGVsc19Vc2Vy&expires=1626433419&signature=ce35c6244683813273382c151cb83fa1937b2c2be4e2e5cef3af486889ba5b16
Enter fullscreen mode Exit fullscreen mode

The URL contains a couple of key parts we need to identify the user type and identifier.

The auth_id is ofcourse our autenticatables identifier. This is what we use to fetch the instance of our authenticatable.

The auth_type is a base64-encoded string of our autenticatable classname. As in the example above the auth_type would decode to App_Models_User. By adding this to our URL we can get the correct guard from the given model, giving us great flexibility to create multiple authenticatable models with each having their own guard, while still being able to login with those guards.

The expires and signature are generated by the temporarySignedUrl method provided by Laravel. To read more about SignedURL’s and how they protect against manipulation, please make sure to check out Laravel’s documentation.

Ok back to the main course.

There are alternative ways to generate a login URL, for example you can use the provided facade:

use Koost89\LoginLinks\Facades\LoginLink;

$user = User::first();
$link = LoginLink::generate($user);
Enter fullscreen mode Exit fullscreen mode

or even with a console command:

php artisan login-links:generate 1
Enter fullscreen mode Exit fullscreen mode

In the console command you can specify the identifier of your App\Models\User class or when you need a different authenticatable, you can specify that with the --class= option

php artisan login-links:generate 1 --class=App\Models\Team
Enter fullscreen mode Exit fullscreen mode

Once you have visited the URL, you should log in as the specified user. If you haven’t changed any configuration, the URL is removed after the first login with that link. By default the URL expires after 120 seconds (2 minutes), but you can simply edit that in the config.

When a URL is not clicked and expires, it isn’t automatically deleted from the database. To make sure your database stays nice and clean Laravel Login Links comes with a command you can run in your scheduler.

php artisan login-links:cleanup
Enter fullscreen mode Exit fullscreen mode

This command deletes all the existing and expired URL’s from your database and if you combine this with Laravel’s scheduler, it helps keeping things clean!

protected function schedule(Schedule $schedule)
{
    $schedule->command('login-links:cleanup')->hourly();
}
Enter fullscreen mode Exit fullscreen mode

Now an additional wish for me was events, I’d like to know when links were being generated but also when they were used. To facilitate this I added both those events in this package.

When you generate a URL, we fire the LoginLinkGenerated event for you to listen to. This event receives the id and the class data for the user it was generated for.

When you click the generated URL, the LoginLinkUsed event is fired which contains the same id and class data. This way we can easily log the flows of our generated URL’s.

While there are great alternatives to this package, I created this because I had very specific wishes for it and creating my own package worked out wonderfully. If you’d wish to read more about it, check it out on github: Login Links for Laravel

Thank you for taking your time to read this post!

Discussion (1)

Collapse
mobunti profile image
ItNuBom

Hi. Have you considered the security aspects of login links that go out in insecure emails ? Specifically that if your email account is hacked, someone can log in to whatever service is being provided without a password, if you pull it down over an insecure protocol someone might snoop on it, or, even the customer not realising that the link auto logs them in and they forward it to a customer and say "check out these people" and suddenly they can log in as you and maybe see your financial transactions or your personal profile, place an order under your name, etc etc

Im not a major security buff but certainly UK wise (and European) with GDPR and PECR, I can't think of anything worse than sending links out by email that log you in.

If the system has a requirement to have security on it in the first place, why circumvent it, especially given most people will tell their browser to remember their login details anyway so a direct link to the correct page which redirects to a login page, you press enter to log in with your details and go back to the correct page.

Also remember that nowadays anti virus systems tend to visit links in emails to check if the page that is being linked to is phishy etc so that means that google, microsoft or mcafee etc can also now log in to your account too ;-(

Im sure it does the job well, Im just highlighting issues that I've come across before that Im not sure can be circumvented, but well done for creating a package and making it available.