0

Laravel's auth system — guards, providers, the Auth facade

Intermediate5 min read·lv-16-001
interviewlaravel-src

Concept

Laravel's authentication system is built around three orthogonal abstractions: guards, providers, and the Auth facade. A guard determines how users are authenticated on each request — it checks the incoming request, extracts credentials or a session identifier, and returns an authenticated user or null. A provider determines how users are actually retrieved from storage — it translates a credential set (typically email + password, or a token) into an Eloquent model or any other user object. The Auth facade (Illuminate\Support\Facades\Auth) is a static proxy over Illuminate\Auth\AuthManager, which manages multiple named guard instances.

Guards are defined in config/auth.php under the guards key. Each guard entry specifies a driver and a provider. Built-in drivers are session (stateful, cookie-based) and token (stateless, bearer token in request header). Sanctum and Passport add their own guard drivers. Providers are defined under the providers key and specify a driver — either eloquent (uses an Eloquent model) or database (uses raw query builder) — and the model or table to query.

AuthManager implements the Manager pattern (extending Illuminate\Support\Manager). When you call Auth::user() or Auth::check(), the manager resolves the default guard specified by config('auth.defaults.guard'), constructs it once, and caches the instance for the request lifecycle. Calling Auth::guard('api') resolves a different named guard. Each guard implements Illuminate\Contracts\Auth\Guard (stateless) or Illuminate\Contracts\Auth\StatefulGuard (stateful, with login(), logout(), attempt(), and viaRemember()).

The Illuminate\Auth\SessionGuard is the concrete class behind the default web guard. It wraps an Illuminate\Contracts\Auth\UserProvider and an Illuminate\Contracts\Session\Session. On each request it checks the session for a stored user id, resolves the user via the provider, and stores the instance in memory so subsequent calls to Auth::user() within the same request are free.

Config keyDefault valuePurpose
auth.defaults.guardwebWhich guard Auth::user() uses
auth.defaults.passwordsusersWhich broker handles password resets
auth.guards.web.driversessionGuard implementation
auth.guards.web.providerusersWhich provider supplies user objects
auth.providers.users.drivereloquentProvider implementation
auth.providers.users.modelApp\Models\UserModel class to query

Code Example

php
<?php

// config/auth.php — illustrating multi-guard setup
return [
    'defaults' => [
        'guard'     => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver'   => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver'   => 'sanctum',  // or 'token', or 'passport'
            'provider' => 'users',
        ],
        'admin' => [
            'driver'   => 'session',
            'provider' => 'admins',   // separate provider/model
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model'  => App\Models\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model'  => App\Models\Admin::class,
        ],
    ],
];

// Reading from the Auth facade
use Illuminate\Support\Facades\Auth;

/** @var \App\Models\User|null */
$user = Auth::user();                  // current user or null
$user = Auth::guard('admin')->user();  // from the admin guard
$id   = Auth::id();                    // shortcut for Auth::user()?->getAuthIdentifier()
$bool = Auth::check();                 // true if a user is authenticated
$bool = Auth::guest();                 // true if NOT authenticated

Interview Q&A

Q: What is the difference between a guard and a provider in Laravel's auth system?

A guard is responsible for the authentication mechanism — it inspects the current request (reads a session, parses a bearer token, checks a cookie) and decides whether the user is authenticated. A provider is responsible for retrieving the user — it translates an identifier or credential into a user object by querying a data source such as an Eloquent model or a database table. Guards delegate to providers; you can mix and match them freely. For example, a session guard backed by an Eloquent provider is the default web setup, while a Sanctum token guard still uses the same Eloquent provider underneath.


Q: How does Auth::guard('api') differ from Auth::user() in a multi-guard application?

Auth::user() resolves the guard named in config('auth.defaults.guard') (usually web) and returns the authenticated user for that guard. Auth::guard('api') returns the guard instance for the api guard without setting it as the default — it acts as a scoped accessor. This matters when a request matches both a session cookie and an API token; calling the wrong guard would return the wrong user or null. In controllers registered under the auth:api middleware, the API guard is automatically resolved, but you can always be explicit with Auth::guard('api')->user().


Q: Where in the request lifecycle does SessionGuard hydrate the authenticated user?

SessionGuard is lazy — it does not query the database at construction time. When Auth::user() is first called during a request (typically by middleware like Illuminate\Auth\Middleware\Authenticate), the guard calls its user() method, which checks an in-memory cache ($this->user). If null, it reads $this->session->get($this->getName()) to get the stored user identifier, then calls $this->provider->retrieveById($id) to fetch the model from the database. The result is cached in $this->user for the rest of the request lifecycle, so subsequent calls to Auth::user() are free.