skip to content

In Laravel's config/auth.php, what is the difference between a guard and a user provider, and what ships by default?

level: juniorimportance: must knowfreq 55%

answer

  1. how vs where
  2. guard: driver plus provider name
  3. web guard uses the session driver
  4. users provider: eloquent, App\Models\User
  5. defaults.guard reads AUTH_GUARD, falls back to web

basics

~20 s

A guard decides how a request is authenticated and remembered; a user provider decides where user records are loaded from. The Laravel 13 skeleton ships one web guard (session driver) pointing at one users provider (eloquent, App\Models\User).

solid answer

~40 s

In `config/auth.php` a **guard** is the component that works out who is making the request: the Laravel 13 skeleton defines a single `web` guard with the `session` driver, so the signed-in user's ID is kept in the session between requests. A **user provider** is the storage side: given an ID or a set of credentials, it loads the user. The skeleton's `users` provider uses the `eloquent` driver with `'model' => env('AUTH_MODEL', User::class)`; a commented-out block shows the `database` driver with a `table` instead. Each guard names its provider through its `provider` key, and `defaults.guard` (`env('AUTH_GUARD', 'web')`) chooses the guard that `Auth::user()` and `$request->user()` use when you name none. Packages plug extra guard drivers into the same file: Sanctum registers `sanctum`, Passport registers `passport`.

go deeper

for a junior

Recall the pair: a guard answers how the request is authenticated, a provider answers where users are loaded from. Know the skeleton's web, session, users and eloquent values.

for a middle

Explain the lookup chain from defaults.guard to the guard's provider key to the model, and why session guards are stateful while token-style guards re-check every request.

for a senior

Show you can read an unfamiliar config/auth.php and say which guard each route family uses, which provider backs it, and what breaks when a package adds its own driver.

for a principal

Argue when a second guard is warranted versus one guard with roles, and how the split keeps transport and storage decisions independent as the product grows.

## Two separate questions Authenticating a request in Laravel means answering two different questions, and `config/auth.php` gives each one its own component: - **How do we know who this request belongs to?** That is the job of a **guard**. A guard inspects the request (a session cookie, a header, a query parameter) and returns the user, or `null`. - **Where do user records live, and how do we load one?** That is the job of a **user provider**. A provider knows how to fetch a user by ID, by credentials such as an email address, or by a "remember me" token, and how to check a password against the stored hash. Keeping them apart means you can change the transport (session versus token) without touching the storage, and change the storage (an Eloquent model versus a plain table) without touching the transport. ## What the Laravel 13 skeleton ships A fresh `laravel/laravel` 13 application has one of each: | Key | Value in the skeleton | Meaning | |---|---|---| | `defaults.guard` | `env('AUTH_GUARD', 'web')` | guard used when code names none | | `guards.web.driver` | `session` | user ID stored in the session | | `guards.web.provider` | `users` | which provider loads the user | | `providers.users.driver` | `eloquent` | load users through a model | | `providers.users.model` | `env('AUTH_MODEL', User::class)` | the model class | The file also carries a `passwords` section for the password broker and `password_timeout`, which belong to the reset and confirmation features rather than to guards. The skeleton's `.env.example` sets none of the `AUTH_*` variables, so the fallbacks above are what runs. ## How one request uses them 1. Code calls `Auth::user()`, `auth()->user()` or `$request->user()` without naming a guard. 2. The auth manager (`Illuminate\Auth\AuthManager`) reads `defaults.guard`, finds `guards.web`, and builds a `SessionGuard` the first time it is asked, caching it for the rest of the request. 3. While building the guard it creates the provider named in `provider`, here an `EloquentUserProvider` for `App\Models\User`. 4. The session guard reads the user ID from the session and asks the provider for `retrieveById($id)`. 5. The provider runs the model query and returns the user, which the guard caches. Name a guard that is not in `guards` and `Auth::guard('x')` throws `InvalidArgumentException` with the message "Auth guard [x] is not defined." ## Session versus token drivers Guard drivers fall into two families: - **Stateful** guards implement `Illuminate\Contracts\Auth\StatefulGuard`. The built-in `session` driver (`SessionGuard`) is the only one in the framework: it has `attempt()`, `login()` and `logout()`, and it remembers the user between requests through the session. - **Stateless** guards implement only `Illuminate\Contracts\Auth\Guard`. They authenticate every request afresh from something the request carries. The framework still contains a basic `token` driver (`TokenGuard`, reading an `api_token` value from the query string, body or bearer header), but the skeleton's comment lists only `session` as supported and the docs send API authentication to **Sanctum** or **Passport**, which register their own `sanctum` and `passport` drivers. A stateless guard has no `login()` method, so "signing in" through it means nothing: each request proves itself again. ## Provider drivers The framework has two provider drivers: - `eloquent` returns instances of the configured model, so the signed-in user has relationships, casts and the `can()` helper. - `database` queries a table through the query builder and returns an `Illuminate\Auth\GenericUser`, a thin object with only the `Authenticatable` methods. An unknown provider driver fails with "Authentication user provider [x] is not defined." ## Common misreadings - Treating "guard" and "provider" as synonyms: the guard never loads user records itself, and the provider never reads cookies or headers. - Assuming one guard per model is mandatory: several guards may share one provider, for example a session guard and a token guard over the same `users` provider. - Editing `AUTH_GUARD` to switch an admin area to another guard: that changes the default for the whole application, not for one set of routes.

  • What happens if you call Auth::guard('admin') when config/auth.php has no admin guard?
    The auth manager looks up `auth.guards.admin`, finds nothing and throws `InvalidArgumentException` with "Auth guard [admin] is not defined." It does not fall back to the default guard. If the guard exists but its `driver` names something neither built in nor registered with `Auth::extend`, the message is "Auth driver [x] for guard [admin] is not defined."
  • Can two guards share the same user provider?
    Yes. A guard only references a provider by name, so a `session` guard for the browser and a token-style guard for an API can both point at `users`. Several guards over one provider is common: the provider only answers where users live, and each guard adds its own way of recognising them.

saying these in an interview costs you the question

  • A guard and a user provider are two names for the same thing.
  • The web guard stores the whole user object in the session.
  • The default users provider queries the users table directly, not through a model.
  • Changing AUTH_GUARD is how you protect just the admin routes with another guard.
  • An undefined guard name silently falls back to the default guard.