Laravel – Custom authentication and what happens under the hood

In this article, we will take a detailed look at how the default authentication works inside Laravel core. But first, we will learn how to create a custom authentication.

Laravel ships in with a great default user authentication. It even creates the views and routes for you if you run

// Create the default views, routes and controller for authentication
php artisan make:auth

All is well as long as we are good with the default users table and its authentication. But what if we want another authentication method. For example, you want to user a separate table called admin for authenticating admin users while keeping the normal users table for front end authentication. Let’s start looking at the config file auth.php in the config folder.

<?php

// The default guard and password set in config/auth.php
'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

The guards and passwords are defined below

<?php

 //guard contains the session driver and the provider
 'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
      ]

The password settings are used for resetting passwords and we will ignore this for simplicity for now. We will get to the driver later in the discussion. Provider is defined below

<?php

//Provider defines the driver (Eloquent or Database) and the Modal used for authentication
'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
     ]

As you can see it defines the eloquent model that is used. Below are the steps needed

 

Step 1

Add a new guard in auth.php

<?php

 // Create an 'admin' guard
 'admin' => [
            'driver' => 'session',
            'provider' => 'admins'
        ],

Step 2

Add provider in auth.php

<?php

 //Use eloquent as the driver and add App\Admin as our model
 'admin' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
        ],

Step 3

Let’s create the modal and migration for our table – admins

//Create our migration and modal
php artisan make:model Admin -m

The -m at the end will create the migration for you. Just copy the migration for the users table for now and run your migration.

Step 4

A normal eloquent modal would extend Illuminate\Database\Eloquent\Model but this won’t do for authentication modals. If you take a look at the User modal you will see that it extends Illuminate\Foundation\Auth\User. So change your modal so,

At the top of the page, you have this

<?php

//We need to extend this class
use Illuminate\Foundation\Auth\User as Authenticatable;

and make the class extend it

<?php

//Modal extends the Auth\User class for leveraging Laravel's built in auth capabilities
 class Admin extends Authenticatable

Step 5

Create a new controller AdminAuthController.php

//Create our new controller, move it to the same folder as the default AuthController if you prefer that
php artisan make:controller AdminAuthController

Add the guard protected property. If this is not defined, Laravel uses the default guard set in auth.php (web)

<?php

//Add guard property to our controller
protected $guard = 'admin';

Don’t forget to add the necessary traits for logging in, registering etc. in the new controller (just like the default AuthController class )

<?php

//Traits for authenticating, registering and login throttling 
use AuthenticatesAndRegistersUsers, ThrottlesLogins;

 

That’s about it! Now you can use Auth::login in the AdminAuthController just like AuthController but it will use the admins table. But what if we want to change the username field to something like user_name (for some weird reason).

 

Changing the username field

Just add a $username propery to your AuthController or AdminAuthController. By default, Laravel uses email as the username field.

<?php

//Add username property if you wish to change the login username db column
protected $username = 'username';

Let’s see how this works. Open up the trait AuthenticatesUsers and go to theĀ  login function. If you trace through it, you will see this.

<?php

//In the AuthenticatesUsers trait, it just returns the property if it exists. Otherwise it defaults to email column.
public function loginUsername()
{
    return property_exists($this, 'username') ? $this->username : 'email';
}

So whatever value you add to the property, will be used as the username. For eg. if you want to use user_name (suppose that you need it like that for some wierd reason), you can do like so.

//Another example for updating the username
protected $username = 'user_name';

 

What happens under the hood?

So how does all this work? In order to understand this, please work along with me and open the files and go through them yourself. Lets begin with the AuthController. Take a look at this trait used by it – AuthenticatesAndRegistersUsers. This trait just has 2 traits, nothing else – AuthenticatesUsers, RegistersUsers. Lets take a look at AuthenticatesUsers. You can see the function login defined in there. It does a bunch of stuff like validation, lets skip to the place where it attempts to log the user in.

<?php

if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
            return $this->handleUserWasAuthenticated($request, $throttles);
}

Take a look at the getGuard function which is called in that

<?php

protected function getGuard()
{
        return property_exists($this, 'guard') ? $this->guard : null;
}

As you can see, it looks for a property called guard and returns its value if it exists. This is why we added the protected $guard property to the AdminAuthController earlier. If we do not define it, the getGuard function always returns null which would mean that the default guard (web) will be used.

So next, we need to figure out what this does

<?php

Auth::guard

That is a facade. If you don’t know what is a facade, take a look at our article about Facades. Take a look at the service provider Illuminate\Auth\AuthServiceProvider::class. You can see that it returns a singleton for the AuthManager class

<?php

return new AuthManager($app);

The AuthManager class is where most of the magic happens. You can see the guard function in there so we have finally found it!

<?php

public function guard($name = null)
{
    $name = $name ?: $this->getDefaultDriver();

    return isset($this->guards[$name])
                    ? $this->guards[$name]
                    : $this->guards[$name] = $this->resolve($name);
}

This doesn’t seem to be doing much. It just populates the guards array property. But let’s have a better look this class. We know that the resolve function is called so lets look at that first,

<?php

$config = $this->getConfig($name); //it gets the config settings of our guard, the driver and provider used.
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';

If you remember our guard settings, the driver was set to session.

<?php

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
      ]

So lets see if there is a method called createSessionDriver. Of course there is! Lets see what it has got.

<?php

$provider = $this->createUserProvider($config['provider']); //createUserProvider is a trait, it defines if we use EloquentUserProvider or the DatabaseUserProvider.
$guard = new SessionGuard($name, $provider, $this->app['session.store']);

Open the SessionGuard class.If you remember, We had figured out that the authentication happens in this line in the AuthManager class

<?php

Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))

The attempt method is defined here. If you had defined the driver as token, the guard class would have been TokenGuard. You can also see functions like user, id etc. So this is where the call goes to when we do – Auth::id() or Auth::user(). Also take a look at the trait GuardHelpers used in the SessionGuard.

 

Now we have figured out what happens under the hood during a normal Laravel authentication. Its always good to get an idea about how things underneath. It gives us the confidence needed when designing complex systems