Create passwordless authentication in Laravel using email only
- Transfer
I recently worked on a project where one of the pain points was the user's password. The administrator added users to the application, so they do not have a password, and it was extremely inconvenient to force them to create a password at the first login after registration.
So, we decided to try the passwordless login method. If you have never had the opportunity to work with this, we will tell you how it works:
On the login page, the user enters his email address, to which he will receive a link to the login. Following the link confirms the identity of the user without the need to enter a password, since the link for each user login is unique.
Let's start creating!

First, we will create our application by connecting authentication:
Now we have all the files necessary for authorization, including views. Let's start with them.
Of course, combining a username with a password is a pretty good idea, but we need to abandon the password field on both forms.
Open the file `resources / views / auth / login.blade.php` and delete the group responsible for entering the password (label, input and wrapper). Save, close.
Now open the file `resources / views / auth / register.blade.php` and delete the groups responsible for entering the password (` password`) and password confirmation (`password-reset`). Save, close.
Later you can add instructions on the login method on the authentication page, as well as post links to reset your password, but this is later.
So, we need to change the route pointing to the entry and registration points. Take a look at the `AuthController` controller.
First, we will notice the `validator` method, which returns the validation of the password field. Since he is responsible for the process of registering an account, we need to get rid of its binding to a password.
Ultimately, the function should look like this:
We will do the same for the `Create` method, bringing it to the form:
As you can see, there are no methods for registering users. They are hidden in the `AuthenticatesAndRegistersUsers` tray, which uses the` AuthenticatesUsers` authentication traits and the `RegistersUsers` registration traits. You can go to the `AuthenticatesUsers` trait and find the user authentication method named` login` at the end of the file.
Everything that happens there is based on secure passwords, although this method can also be replaced ...
The aim of our new method is to send a user login link to the system’s email. Let's go back to the `AuthController` controller and add a login method that overrides the` login` in the AuthenticatesUsers:
Confirm the reality of the email address for a registered user is very simple:
Next, we need to send the user a link to the entrance. It will take a little longer.
If you are familiar with the form of the database structure `password_reset`, then it will be easier for you, because we will create something similar. Every time someone tries to log into the system, we need to add an entry to the table, which will record the email address and unique token sent in the email as a URL, as well as the date of creation and the lifetime of the entry.
In the end, we will use the URL to create (and verify), for example: `myapp.com / auth / email-authenticate / 09ajfpoib23li4ub123p984h1234`. Since the token's life is limited, we must associate this URL with a specific user, tracking email, token and creation date for each table entry.
So, create a migration for him:
And add a few fields to it:
Now, let's create a model.
Edit the file (`app / EmailLogin.php`) and make it simple for us by creating an instance with the desired properties:
And when we want to find the user, we should use the email, not the identifier, manually linking the email column:
Now we are ready to create an email message. We will use a URL containing a unique token generated in advance.
You need to understand how we will create and store the token. To do this, we need to create an instance of `EmailLogin`, so let's get started:
Let's add this method to `EmailLogin`:
We generate a random token and instantiate the `EmailToken` class, getting it back.
So, we need to use `EmailToken` to generate the URL before sending the message to the user.
Let's create a route for it:
... and add a method to the controller for this route to work:
... and add the `validFromToken` method to verify the token:
Now we have an incoming route, taking into account the relevance of each token. If the token is relevant, the user will be redirected to the address `mysite.ru / home`.
Well, let's send an email.
Add the call call call to our controller:
... and create a template:
You can arrange the template in any convenient way, but we just use the text: “Hey, we sent the soap to check it. It's all."
Take a look at our system. We have a new `login` method in the` AuthController` controller:
We created several views, updating the existing ones (we removed the password entries in them). Also created a new route in `/ auth / email-authenticate`. And they also created the `EmailLogin` migration with a class of all its needs.
And ... profit! Put all the examples in your code and get a fully functional passwordless login system.
To register a user, you only need to find out their email address. And during authorization, in addition to their email address, you will no longer need to remember and enter anything. No more forgotten passwords. Boom!
When translating the article, information was adapted for better readability of Russian-speaking users.
So, we decided to try the passwordless login method. If you have never had the opportunity to work with this, we will tell you how it works:
On the login page, the user enters his email address, to which he will receive a link to the login. Following the link confirms the identity of the user without the need to enter a password, since the link for each user login is unique.
Let's start creating!

New application and make: auth
First, we will create our application by connecting authentication:
laravel new medium-login
cd medium-login
php artisan make:auth
Now we have all the files necessary for authorization, including views. Let's start with them.
Change login and registration page
Of course, combining a username with a password is a pretty good idea, but we need to abandon the password field on both forms.
Open the file `resources / views / auth / login.blade.php` and delete the group responsible for entering the password (label, input and wrapper). Save, close.
Now open the file `resources / views / auth / register.blade.php` and delete the groups responsible for entering the password (` password`) and password confirmation (`password-reset`). Save, close.
Later you can add instructions on the login method on the authentication page, as well as post links to reset your password, but this is later.
Change Registration Routes
So, we need to change the route pointing to the entry and registration points. Take a look at the `AuthController` controller.
First, we will notice the `validator` method, which returns the validation of the password field. Since he is responsible for the process of registering an account, we need to get rid of its binding to a password.
Ultimately, the function should look like this:
// app/http/Controllers/Auth/AuthController.php
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
]);
}
We will do the same for the `Create` method, bringing it to the form:
// app/http/Controllers/Auth/AuthController.php
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
]);
}
Overlap of the login path
As you can see, there are no methods for registering users. They are hidden in the `AuthenticatesAndRegistersUsers` tray, which uses the` AuthenticatesUsers` authentication traits and the `RegistersUsers` registration traits. You can go to the `AuthenticatesUsers` trait and find the user authentication method named` login` at the end of the file.
Everything that happens there is based on secure passwords, although this method can also be replaced ...
The aim of our new method is to send a user login link to the system’s email. Let's go back to the `AuthController` controller and add a login method that overrides the` login` in the AuthenticatesUsers:
// app/http/Controllers/Auth/AuthController.php
public function login(Request $request)
{
// validate that this is a real email address
// send off a login email
// show the users a view saying "check your email"
}
Email Verification
Confirm the reality of the email address for a registered user is very simple:
$this->validate($request, ['email' => 'required|email|exists:users']);
Sending Email
Next, we need to send the user a link to the entrance. It will take a little longer.
Creating a structure for the formation and verification of email tokens
If you are familiar with the form of the database structure `password_reset`, then it will be easier for you, because we will create something similar. Every time someone tries to log into the system, we need to add an entry to the table, which will record the email address and unique token sent in the email as a URL, as well as the date of creation and the lifetime of the entry.
In the end, we will use the URL to create (and verify), for example: `myapp.com / auth / email-authenticate / 09ajfpoib23li4ub123p984h1234`. Since the token's life is limited, we must associate this URL with a specific user, tracking email, token and creation date for each table entry.
So, create a migration for him:
php artisan make:migration create_email_logins_table --create=email_logins
And add a few fields to it:
Schema::create('email_logins', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token')->index();
$table->timestamps();
});
Note: if desired, you can use the value of the `id` column instead of a token, but there are several reasons for better options. In any case, you decide.
Now, let's create a model.
php artisan make:model EmailLogin
Edit the file (`app / EmailLogin.php`) and make it simple for us by creating an instance with the desired properties:
class EmailLogin extends Model
{
public $fillable = ['email', 'token'];
}
And when we want to find the user, we should use the email, not the identifier, manually linking the email column:
class EmailLogin extends Model
{
public $fillable = ['email', 'token'];
public function user()
{
return $this->hasOne(\App\User::class, 'email', 'email');
}
}
Token Creation
Now we are ready to create an email message. We will use a URL containing a unique token generated in advance.
You need to understand how we will create and store the token. To do this, we need to create an instance of `EmailLogin`, so let's get started:
public function login()
{
$this->validate($request, ['email' => 'required|email|exists:users']);
$emailLogin = EmailLogin::createForEmail($request->input('email'));
}
Let's add this method to `EmailLogin`:
class EmailLogin extends Model
{
...
public static function createForEmail($email)
{
return self::create([
'email' => $email,
'token' => str_random(20)
]);
}
}
We generate a random token and instantiate the `EmailToken` class, getting it back.
Formation of URLs for sending by email
So, we need to use `EmailToken` to generate the URL before sending the message to the user.
public function login()
{
$this->validate($request, ['email' => 'required|email|exists:users']);
$emailLogin = EmailLogin::createForEmail($request->input('email'));
$url = route('auth.email-authenticate', [
'token' => $emailLogin->token
]);
}
Let's create a route for it:
// app/Http/routes.php
Route::get('auth/email-authenticate/{token}', [
'as' => 'auth.email-authenticate',
'uses' => 'Auth\AuthController@authenticateEmail'
]);
... and add a method to the controller for this route to work:
class AuthController
{
...
public function authenticateEmail($token)
{
$emailLogin = EmailLogin::validFromToken($token);
Auth::login($emailLogin->user);
return redirect('home');
}
}
... and add the `validFromToken` method to verify the token:
class EmailLogin
{
...
public static function validFromToken($token)
{
return self::where('token', $token)
->where('created_at', '>', Carbon::parse('-15 minutes'))
->firstOrFail();
}
Now we have an incoming route, taking into account the relevance of each token. If the token is relevant, the user will be redirected to the address `mysite.ru / home`.
Well, let's send an email.
Sending letter
Add the call call call to our controller:
public function login()
{
...
Mail::send('auth.emails.email-login', ['url' => $url], function ($m) use ($request) {
$m->from('noreply@myapp.com', 'MyApp');
$m->to($request->input('email'))->subject('MyApp Login');
});
... and create a template:
Log in to MyApp here: {{ $url }}Return template
You can arrange the template in any convenient way, but we just use the text: “Hey, we sent the soap to check it. It's all."
return 'Login email sent. Go check your email.';
Joint entrance
Take a look at our system. We have a new `login` method in the` AuthController` controller:
public function login(Request $request)
{
$this->validate($request, ['email' => 'required|exists:users']);
$emailLogin = EmailLogin::createForEmail($request->input('email'));
$url = route('auth.email-authenticate', [
'token' => $emailLogin->token
]);
Mail::send('auth.emails.email-login', ['url' => $url], function ($m) use ($request) {
$m->from('noreply@myapp.com', 'MyApp');
$m->to($request->input('email'))->subject('MyApp login');
});
return 'Login email sent. Go check your email.';
}
We created several views, updating the existing ones (we removed the password entries in them). Also created a new route in `/ auth / email-authenticate`. And they also created the `EmailLogin` migration with a class of all its needs.
It's all!
And ... profit! Put all the examples in your code and get a fully functional passwordless login system.
To register a user, you only need to find out their email address. And during authorization, in addition to their email address, you will no longer need to remember and enter anything. No more forgotten passwords. Boom!
From translator
When translating the article, information was adapted for better readability of Russian-speaking users.