Laravel Auth
Laravel has a nice out of the box authentication system that has been around for awhile and is always being worked on. Though it is more extendable and configurable now doesn't entirely mean it is crazy complicated.
Overview
In most of your applications you will be dealing with the authentication system. You probably have a working registration and login system as well as having routes protected from unauthenticated users. You can get this all up and working quickly and without thinking much about how this is all implemented.
When you are dealing with the authentication system there are a few pieces in play. We have a User Provider
and a Guard
that we are configuring as a guard
for the AuthManager
which is helping manage all these pieces for us and provides us with the necessary guard
s.
Guess what? I am the manager, b
The AuthManager
(Illuminate\Auth\AuthManager
) is the main starting location for dealing with the authentication system. The AuthManager
has the ability to create drivers (Guard
classes) and providers (User Providers
). When we ask it for a guard
it can create the driver and provider needed based on the configuration and return us a Guard
ready to use. The manager also keeps track of the current User Resolver to be shared with the other services.
If you plan on extending the authentication system you will be doing that via this manager class.
Find me that user
A big piece of this system is being able to query some type of storage for information about a User. The User Provider
is the interface to which the Guard
will use to retrieve a user from persistent storage and validate any credentials. Laravel comes with two User Providers
out of the box: (in Illuminate\Auth
)
-
EloquentUserProvider
uses Eloquent Models when interacting with the persistence layer. This provider returns an instance of the Eloquent Model defined in the configuration as the User. -
DatabaseUserProvider
uses the Query Builder directly to interact with the persistence layer and will return aGenericUser
as the representation of the authenticated User.
The EloquentUserProvider
requires a model
variable to be defined in its configuration and the DatabaseUserProvider
needs a table
variable to be defined.
Who's watching the door?
The Guard
s (driver
in the guards
config) implement how users are authenticated based on the request. They do the work of checking the conditions and making the decisions based on what they are provided. They deal with the current request and use a User Provider
to interact with a persistence layer. Laravel also comes with two Guard
s out of the box: (in Illuminate\Auth
)
-
SessionGuard
uses the session to check if the current user is already authenticated or checks via a recaller cookie, 'remember me', and will use the session to save that state of the user after a successful login. -
TokenGuard
uses a token sent with each request to "login" the user by token to the system for the current request.
The auth
Config
The configuration we have for authentication has a little more going on but its those parts above (Guard
, User Provider
) being configured individually and assembled together.
From config/auth.php
:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
Broken down:
-
guards
- The name you use when referencing these combinations ofdriver
andprovider
when using theAuthManager
-
driver
-Guard
classes -session
,api
, ... -
provider
(referencesproviders
below)
-
-
providers
- The name we assign to the User Provider and configuration combination-
driver
-User Provider
-eloquent
,database
, ... - additional configuration variables for the driver
'model' => ...
or'table' => ...
-
Defaults
Using the defaults setup you will be dealing with the App\User
model, via the EloquentUserProvider
, and the SessionGuard
. This is the basic normal setup you would have been used to for years. The session will be used to identify the user as authenticated.
From config/auth.php
:
'defaults' => [
'guard' => 'web', // ['driver' => 'session', 'provider' => 'users']
'passwords' => 'users',
],
Interacting
Auth
and auth()
What is going on with our facade and our helper function that Laravel has provided us?
- If we check our trusty Facade Class Reference from the documentation we find out that the
Auth
Facade's root class isIlluminate\Auth\AuthManager
that is the bindingauth
. - Jumping into
Illuminate\Auth\AuthServiceProvider
we seeauth
being bound as a singleton and that closure returns aIlluminate\Auth\AuthManager
instance. - If we check
/Illuminate/Foundation/helpers.php@auth
we see an instance ofIlluminate\Contracts\Auth\Factory
is being resolved from the container. - And some aliasing ...
Illuminate\Foundation\Application@registerCoreContainerAliases
-
'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class]
-
Behind the scenes
You are interacting with the AuthManager
but mostly a particular guard
when making calls to the manager. Do to the lovely nature of our dynamic __call
method any methods that aren't accessible on AuthManager
are being called upon the default guard
. This is where the 'defaults' from the the config come into play.
-
Auth::attempt(...)
->Auth::guard()->attempt(...)
-
Auth::check()
->Auth::guard()->check()
The 'default' guard can and will be adjusted at run time as needed. This yields the advantage of not needing to know exactly which guard was actually used to do the authentication.
The User Resolver
The User Resolver is the method which is used to resolve the current authenticated user. This gets adjusted through the lifecycle of the application depending upon what guard ends up authenticating. Examples that use this resolver:
$request->user();
Auth::user();
Other services will use this resolver to get the current user. From the Illuminate\Auth\AuthManager
source:
/**
* The user resolver shared by various services.
*
* Determines the default user for Gate, Request, and the Authenticatable contract.
*
* @var \Closure
*/
protected $userResolver;
/**
* Create a new Auth manager instance.
*
* @param \Illuminate\Foundation\Application $app
* @return void
*/
public function __construct($app)
{
$this->app = $app;
$this->userResolver = function ($guard = null) {
return $this->guard($guard)->user();
};
}
How the resolver is ... resolved?
When it comes to Request@user
the AuthServiceProvider
sets a rebinding on request
to call setUserResolver
on the request
. A closure is passed to setUserResolver
which calls the function returned from AuthManager@userResolver
to get the current resolver registered with the manager. The closure passed to setUserResolver
receives a $guard
variable.
Illuminate\Contracts\Auth\Authenticatable
is bound to call the function returned from AuthManager@userResolver
.
Dynamic resolvers
You may have wondered why when using the api
group of middleware that $request->user()
returns the correct user from the api
guard
and doesn't use the default web
guard
.
When we use the auth
(Authenticate
) middleware depending upon the guard (if one is passed) this will set the current userResolver
based on the guard by calling AuthManager@shouldUse($guard)
. This tells the manager to now use this guard
as the default, which means it can resolve the current user without specifying which guard
to use when calling it.
Wrap it up, b
The AuthManager
is the main part of the authentication system. It pulls all the pieces together into something you can use (guard
[driver
and provider
]) through out your application. It has changed over the years to allow you to extend and change what pieces it uses internally. From the outside it looks very much the same.
You really don't need to know all of this to use the authentication system, but it is nice to know what is going on.