Laravel Auth — how does it happen?
Laravel is definitely a good framework. That’s what people say. I can’t disagree. The only framework I know and I code. I didn’t even write any project with raw PHP. I have been working with PHP since 2013 September. I can still remember that. I learnt python before PHP. Someone said Python & Django has great demand on job market. One of the mostly paid jobs. At the end of the day salary matters (for almost everybody). So I moved from Java (I don’t like java that much) to Python. Learnt python, did some coding. Mean time, I had to show a project to my university teacher, a web application. Didn’t know how to implement using python. Someone said before you move to django, learn MVC. Damn. That’s not easy for me. Overwhelming thing. Then I thought to give a try to PHP. And after then since now, I couldn’t move from PHP to any other language. It’s a story. Will take the whole night if you read. Let’s move to the main points now.
Laravel, that works like magic. Obviously true. As I only know Laravel as a framework, I can’t argue over the learning curve about other frameworks. If you just want to get the job done it is simple enough to do with Laravel. Under the hood, it works so many things. I was working on a project where I wanted to use Auth
facade to validate & get the user. But the project’s database structure was different. So I had to dig in how Laravel’s Auth works. You can read the article — Laravel API — Authenticate user with custom driver & different table using auth middleware. So I will try to explain what I have learned.
Laravel boots the application when a request is passed to public/index.php
. It loads up the application from bootstrap/app.php
.
<?php
// laravel 5.5.0 - line:38
// public/index.php
$app = require_once __DIR__.’/../bootstrap/app.php’;
In bootstrap/app.php
it instantiates Illuminate\Foundation\Application
. If you just look into that class constructor, you’ll see that it registers base service providers & core container aliases. Inside the registerCoreContainerAliases
method, it registers the core container aliases. The first element of the array values are the concrete class those are resolved later under the hood for the facades to work.
For the Auth
Facade or app(‘auth’)
or the app()->make(‘auth’)
the underlying class is Illuminate\Auth\AuthManager
. So, all the calls we make through these, AuthManager
is going to handle those calls.
- First things first, A web browser initiates a call to server. (So many things here, I want to how! Check the link included.)
index.php
inside thepublic
folder gets the request. (Apache/Nginx thing. All calls redirected toindex.php
file.)- Loads
bootstrap/app.php
inside the project. Illuminate\Foundation\Application
instantiated. Does whatever is said to do. Loads containers, basic service providers & container aliases.- Besides many things, It creates a singleton of
App\Http\Kernel
which is actually an extension (inheritance) oflluminate\Foundation\Http\Kernel
. - Comes back to
public/index.php
, makes (resolves) instance ofIlluminate\Contracts\Http\Kernel
and in return getsApp\Http\Kernel
. - Calls the
handle
method of the resolved kernel (insideIlluminate\Foundation\Http\Kernel)
. - Call moves to
sendRequestThroughRouter
method. Checks if the middleware should be skipped or not (On test cases while usingIlluminate\Foundation\Testing\WithoutMiddleware
trait). If not then pipes through the middlewares. If you want to know what Pipelining is in Laravel, check THIS LINK. Before checking any route/named middleware, it checks these Middlewares first. Check the middleware list here. - Pipelining through all the middlewares, it dispatches to router class.
- Then the router class tries to match the ROUTE. By default, for web Laravel loads 6 middlewares & for api it loads 2 middlewares. From the route it tries to get the route middleware (if added). Again pipelines the middlewares for the current route.
Where is the AUTH?
Suppose we have two routes like below,
<?php
// routes/web.php
Route::get('users', 'UserController@getUsers')->middleware('auth');// routes/api.php
Route::get('users', 'UserController@getUsers')->middleware('auth:api');
As I said before after coming to 10, it finds that the route has any assigned middlewares. For our example auth middleware applied to it. The responsibility of auth middleware is handled by the Illuminate\Auth\Middleware\Authenticate
class (A link is attached to it). Now, the Authenticate
middleware gets to run and the handle method is fired from the pipeline. (You’ll get to know about this from the linked article above or from code).
- After coming to
Authenticate::handle
the call is moved toauthenticate
method of the same class. - Checks if any guard (auth:web, auth:api) is assigned or not. If not, then the
AuthManager::authenticate
is called which in turn becomes like$this->guard()->{$method}(…$parameters); //or $this->guard()->authenticate()
(methods are on AuthManager). Or if any guard is applied checks for the first guard which can return an user.
Here, guard
method inside the AuthManager
class is the decider of which guard to use. It resolves which provider to use. If no guard is given for the auth, then it takes the default guard driver from config/auth.php
. When it tries to resolve the guard, it checks if any guard name is defined inside the config/auth.php
within the key guards
. If found, it checks if it is a custom created guard or not. If yes, then calls the custom created auth guard with the app instance, name of the guard and configuration stored in config/auth.php
for the guard. And if not custom, it tries to check if any shipped guard was used with ‘createGuard_driver_from_configDriver’ method. If you use session driver for the guard, then createSessionDriver
method will be called or createTokenDriver
for the token driver. Both of them creates instance an implementation of Illuminate\Contracts\Auth\UserProvider
based on the provider driver which is responsible for the authentication & user’s operation based on credentials.
If the guard
method mentioned on previous paragraph can return a TRUTHY value, then that GUARD for which the truthy value was returned will be used throughout the application’s lifetime. If a FALSELY value was returned, then the middleware will assume that the user cannot be authenticated, and throws AuthenticationException
.
So far I tried to show you how the full middleware part is checked. BTW, the guard
middleware is also the same but does the reverse thing. The auth
middleware checks if the user can log in and the guest
middleware checks if the user can log it, he is not permitted. Just check the class and you’ll get it easily now.
[
// ...
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
// ...
]
That’s it for now. If you think I messed up or it’s actually wrong, let me know. I will try to update it. Thanks.