Greetings! I'm Aneesh Sreedharan, CEO of 2Hats Logic Solutions. At 2Hats Logic Solutions, we are dedicated to providing technical expertise and resolving your concerns in the world of technology. Our blog page serves as a resource where we share insights and experiences, offering valuable perspectives on your queries.
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
1 2 | // 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.
1 2 3 4 5 6 7 | <?php // The default guard and password set in config/auth.php 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], |
The guards and passwords are defined below
1 2 3 4 5 6 7 8 9 | <?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
1 2 3 4 5 6 7 8 9 | <?php //Provider defines the driver (Eloquent or Database) and the Modal used for authentication 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => AppUser::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
1 2 3 4 5 6 7 | <?php // Create an 'admin' guard 'admin' => [ 'driver' => 'session', 'provider' => 'admins' ], |
Step 2
Add provider in auth.php
1 2 3 4 5 6 7 | <?php //Use eloquent as the driver and add AppAdmin as our model 'admin' => [ 'driver' => 'eloquent', 'model' => AppAdmin::class, ], |
Step 3
Let’s create the modal and migration for our table – admins
1 2 | //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 IlluminateDatabaseEloquentModel but this won’t do for authentication modals. If you take a look at the User modal you will see that it extends IlluminateFoundationAuthUser. So change your modal so,
At the top of the page, you have this
1 2 3 4 | <?php //We need to extend this class use IlluminateFoundationAuthUser as Authenticatable; |
and make the class extend it
1 2 3 4 | <?php //Modal extends the AuthUser class for leveraging Laravel's built in auth capabilities class Admin extends Authenticatable |
Step 5
Create a new controller AdminAuthController.php
1 2 | //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)
1 2 3 4 | <?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 )
1 2 3 4 | <?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.
1 2 3 4 | <?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.
1 2 3 4 5 6 7 | <?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.
1 2 | //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.
1 2 3 4 5 | <?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
1 2 3 4 5 6 | <?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
1 2 3 | <?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 IlluminateAuthAuthServiceProvider::class. You can see that it returns a singleton for the AuthManager class
1 2 3 | <?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!
1 2 3 4 5 6 7 8 9 10 | <?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,
1 2 3 4 | <?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.
1 2 3 4 5 6 7 8 | <?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.
1 2 3 4 | <?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
1 2 3 | <?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