Passer au contenu principal

Structure interne du système d’authentification Laravel

La façade Auth et AuthManager

La façade Auth est un proxy vers Illuminate\Auth\AuthManager. AuthManager gère plusieurs guards suivant un pattern de driver et instancie/cache l’instance de guard appropriée en se basant sur la configuration de config/auth.php.
// Fonctionnement interne de Auth::guard('web') (version simplifiée de AuthManager::guard())
public function guard($name = null)
{
    $name = $name ?: $this->getDefaultDriver();

    return $this->guards[$name] ?? ($this->guards[$name] = $this->resolve($name));
}
resolve() lit la clé driver dans le tableau guards de config/auth.php et invoque la closure de fabrique correspondante. Les drivers intégrés session et token sont enregistrés selon le même mécanisme.

Différence entre les interfaces Guard et StatefulGuard

Un guard d’authentification Laravel doit au minimum implémenter Illuminate\Contracts\Auth\Guard. S’il doit maintenir une session, il implémente StatefulGuard.
interface Guard
{
    // Vérifie s'il existe un utilisateur authentifié
    public function check();

    // Vérifie si l'utilisateur est invité (non authentifié)
    public function guest();

    // Retourne l'utilisateur authentifié actuel (null si non authentifié)
    public function user();

    // Retourne l'ID de l'utilisateur authentifié actuel
    public function id();

    // Vérifie si les identifiants fournis sont valides (sans connexion)
    public function validate(array $credentials = []);

    // Vérifie si un utilisateur est défini (utilisable même sans connexion via setUser)
    public function hasUser();

    // Définit manuellement l'utilisateur authentifié
    public function setUser(Authenticatable $user);
}
StatefulGuard hérite de Guard et ajoute les méthodes nécessaires pour maintenir l’état de connexion via session ou cookies.
interface StatefulGuard extends Guard
{
    // Valide les identifiants et connecte l'utilisateur (avec flag remember)
    public function attempt(array $credentials = [], $remember = false);

    // Authentifie pour une seule requête sans sauvegarder la session
    public function once(array $credentials = []);

    // Connecte directement une instance d'utilisateur
    public function login(Authenticatable $user, $remember = false);

    // Connecte en spécifiant un ID
    public function loginUsingId($id, $remember = false);

    // Authentifie pour une seule requête en spécifiant un ID
    public function onceUsingId($id);

    // Vérifie si la connexion s'est faite via le cookie « rester connecté »
    public function viaRemember();

    // Déconnexion
    public function logout();
}
Pour un guard qui n’a pas besoin de session (authentification API, token personnalisé, etc.), il suffit d’implémenter Guard. Si vous avez besoin de session (par exemple une connexion administrateur), implémentez StatefulGuard.

Implémentation d’un guard personnalisé

Le trait GuardHelpers

Comme les méthodes check(), guest(), id(), hasUser() de l’interface Guard ont presque toujours la même implémentation, Laravel fournit le trait Illuminate\Auth\GuardHelpers. En l’utilisant, vous pouvez limiter les méthodes obligatoires à user() et validate().
// Implémentation par défaut fournie par GuardHelpers (extrait)
trait GuardHelpers
{
    protected $user;

    public function check(): bool
    {
        return ! is_null($this->user());
    }

    public function guest(): bool
    {
        return ! $this->check();
    }

    public function id(): mixed
    {
        return $this->user()?->getAuthIdentifier();
    }

    public function hasUser(): bool
    {
        return ! is_null($this->user);
    }

    public function setUser(Authenticatable $user): static
    {
        $this->user = $user;
        return $this;
    }
}

Exemple d’implémentation d’un guard d’authentification par token API

En s’inspirant de la conception de TokenGuard, nous implémentons un guard d’authentification par token API simple. Il récupère le token depuis un en-tête de requête ou un paramètre de requête, puis résout l’utilisateur via un UserProvider.
1

Créer la classe de guard

Créez la classe de guard dans le répertoire app/Auth.
<?php

namespace App\Auth;

use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;

class ApiTokenGuard implements Guard
{
    use GuardHelpers;

    protected Request $request;

    public function __construct(UserProvider $provider, Request $request)
    {
        $this->provider = $provider;
        $this->request = $request;
    }

    /**
     * Retourne l'utilisateur actuellement authentifié
     */
    public function user(): ?\Illuminate\Contracts\Auth\Authenticatable
    {
        // Retourne l'utilisateur mis en cache s'il existe
        if (! is_null($this->user)) {
            return $this->user;
        }

        $token = $this->getTokenForRequest();

        if (empty($token)) {
            return null;
        }

        // Récupère l'utilisateur via UserProvider
        $this->user = $this->provider->retrieveByCredentials([
            'api_token' => $token,
        ]);

        return $this->user;
    }

    /**
     * Valide uniquement les identifiants (pas de connexion)
     */
    public function validate(array $credentials = []): bool
    {
        if (empty($credentials['api_token'])) {
            return false;
        }

        return (bool) $this->provider->retrieveByCredentials($credentials);
    }

    /**
     * Récupère le token depuis la requête
     *
     * Ordre de priorité : en-tête Bearer → paramètre de requête → corps de la requête
     */
    protected function getTokenForRequest(): ?string
    {
        $token = $this->request->bearerToken();

        if (empty($token)) {
            $token = $this->request->query('api_token');
        }

        if (empty($token)) {
            $token = $this->request->input('api_token');
        }

        return $token ?: null;
    }

    /**
     * Remplace l'instance de requête
     */
    public function setRequest(Request $request): static
    {
        $this->request = $request;
        return $this;
    }
}
2

Enregistrer le guard dans un service provider

Enregistrez le guard avec Auth::extend() dans la méthode boot() de AppServiceProvider.
<?php

namespace App\Providers;

use App\Auth\ApiTokenGuard;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Auth::extend('api-token', function (Application $app, string $name, array $config) {
            // Auth::createUserProvider() résout le provider défini dans la config
            $provider = Auth::createUserProvider($config['provider'] ?? 'users');

            return new ApiTokenGuard($provider, $app->make('request'));
        });
    }
}
Auth::createUserProvider() lit la configuration providers de config/auth.php et retourne l’instance de UserProvider correspondante. Sauf si vous créez un provider personnalisé, cet appel vous permet d’utiliser le EloquentUserProvider standard.
3

Configurer le guard dans config/auth.php

Ajoutez le nouveau guard dans config/auth.php.
'guards' => [
    'web' => [
        'driver'   => 'session',
        'provider' => 'users',
    ],

    // Guard personnalisé ajouté
    'api' => [
        'driver'   => 'api-token', // doit correspondre au premier argument de Auth::extend()
        'provider' => 'users',
    ],
],
4

Appliquer le guard aux routes

Spécifiez le nom du guard dans le middleware auth.
// routes/api.php
use Illuminate\Support\Facades\Route;

Route::middleware('auth:api')->group(function () {
    Route::get('/user', function () {
        return auth()->user();
    });

    Route::get('/posts', [\App\Http\Controllers\PostController::class, 'index']);
});
Pour utiliser un guard spécifique dans un contrôleur ou du code, appelez Auth::guard('api') ou auth('api').
$user = Auth::guard('api')->user();

Guard simplifié via une closure

Avec Auth::viaRequest(), vous pouvez définir un guard simple avec juste une closure, sans créer de classe. Utile pour prototyper ou pour des authentifications très simples.
use Illuminate\Http\Request;
use App\Models\User;

// Dans AppServiceProvider::boot()
Auth::viaRequest('custom-token', function (Request $request): ?User {
    $token = $request->bearerToken();

    if (empty($token)) {
        return null;
    }

    return User::where('api_token', $token)->first();
});
Configuration dans config/auth.php :
'guards' => [
    'api' => [
        'driver' => 'custom-token',
    ],
],
Les guards définis via Auth::viaRequest() n’utilisent pas de UserProvider, donc les méthodes du provider comme retrieveById() ne fonctionneront pas. En production, il est recommandé d’utiliser un guard basé sur une classe avec Auth::extend().

Implémentation d’un UserProvider personnalisé

Pour récupérer les informations utilisateur depuis une source autre que la base de données (API externe, LDAP, etc.), implémentez l’interface Illuminate\Contracts\Auth\UserProvider.
<?php

namespace App\Auth;

use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;

class ApiUserProvider implements UserProvider
{
    public function __construct(
        protected string $apiBaseUrl,
        protected string $model = User::class,
    ) {}

    /**
     * Récupère un utilisateur par son ID
     */
    public function retrieveById(mixed $identifier): ?Authenticatable
    {
        return ($this->model)::find($identifier);
    }

    /**
     * Récupère un utilisateur via le token « rester connecté »
     * Pour les guards sans session, il suffit de retourner null
     */
    public function retrieveByToken(mixed $identifier, string $token): ?Authenticatable
    {
        return null;
    }

    /**
     * Met à jour le token « rester connecté »
     * Pour les guards sans session, on peut ne rien faire
     */
    public function updateRememberToken(Authenticatable $user, string $token): void {}

    /**
     * Récupère un utilisateur par ses identifiants
     * Appelé depuis validate() et user() du guard
     */
    public function retrieveByCredentials(array $credentials): ?Authenticatable
    {
        if (empty($credentials['api_token'])) {
            return null;
        }

        // Exemple : valider le token via une API externe et récupérer les infos utilisateur
        $response = \Illuminate\Support\Facades\Http::withToken($credentials['api_token'])
            ->get("{$this->apiBaseUrl}/auth/me");

        if (! $response->successful()) {
            return null;
        }

        $data = $response->json();

        // Lier avec un utilisateur en DB locale ou générer un modèle dynamiquement
        return User::firstOrCreate(
            ['external_id' => $data['id']],
            ['name' => $data['name'], 'email' => $data['email']],
        );
    }

    /**
     * Valide les identifiants de l'utilisateur récupéré
     * Pour une authentification par token, true suffit si retrieveByCredentials a réussi
     */
    public function validateCredentials(Authenticatable $user, array $credentials): bool
    {
        return true;
    }

    /**
     * Vérifie si un rehachage du mot de passe est nécessaire
     * Toujours false pour une authentification par token
     */
    public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false): void {}
}

Enregistrement du UserProvider personnalisé

// AppServiceProvider::boot()
Auth::provider('api-user', function (Application $app, array $config) {
    return new \App\Auth\ApiUserProvider(
        config('services.auth_api.base_url'),
        $config['model'] ?? \App\Models\User::class,
    );
});
Ajoutez dans la section providers de config/auth.php :
'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model'  => App\Models\User::class,
    ],

    // Provider personnalisé
    'api-users' => [
        'driver' => 'api-user',
        'model'  => App\Models\User::class,
    ],
],
Combinez guard et provider :
'guards' => [
    'api' => [
        'driver'   => 'api-token',
        'provider' => 'api-users', // spécifie le provider personnalisé
    ],
],

Cas d’usage pratiques

Authentification multiple (guards séparés pour administrateurs et utilisateurs)

1

Créer le modèle Admin

Préparez un modèle Eloquent pour les administrateurs. Hériter de Authenticatable permet l’intégration avec le système Auth.
php artisan make:model Admin -m
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    protected $fillable = ['name', 'email', 'password'];

    protected $hidden = ['password', 'remember_token'];
}
2

Configurer config/auth.php

'guards' => [
    'web' => [
        'driver'   => 'session',
        'provider' => 'users',
    ],
    'admin' => [
        'driver'   => 'session',
        'provider' => 'admins', // provider pour les admins
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model'  => App\Models\User::class,
    ],
    'admins' => [
        'driver' => 'eloquent',
        'model'  => App\Models\Admin::class, // modèle d'administrateur
    ],
],
3

Configurer les routes et middlewares

// routes/web.php

// Routes pour utilisateurs (guard web par défaut)
Route::middleware('auth')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
});

// Routes pour administrateurs (guard admin)
Route::prefix('admin')->middleware('auth:admin')->group(function () {
    Route::get('/dashboard', [AdminDashboardController::class, 'index']);
});
4

Écrire le processus de connexion en spécifiant le guard

<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    public function store(Request $request)
    {
        $credentials = $request->validate([
            'email'    => 'required|email',
            'password' => 'required',
        ]);

        // Faire attempt en spécifiant explicitement le guard admin
        if (Auth::guard('admin')->attempt($credentials)) {
            $request->session()->regenerate();
            return redirect()->intended('/admin/dashboard');
        }

        return back()->withErrors(['email' => 'Les informations de connexion sont incorrectes.']);
    }

    public function destroy(Request $request)
    {
        Auth::guard('admin')->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return redirect('/admin/login');
    }
}

Authentification API externe via token JWT

Exemple d’implémentation d’un guard personnalisé utilisant un service JWT externe.
<?php

namespace App\Auth;

use App\Models\User;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class JwtGuard implements Guard
{
    use GuardHelpers;

    protected Request $request;
    protected ?array $payload = null;

    public function __construct(UserProvider $provider, Request $request)
    {
        $this->provider = $provider;
        $this->request  = $request;
    }

    public function user(): ?\Illuminate\Contracts\Auth\Authenticatable
    {
        if (! is_null($this->user)) {
            return $this->user;
        }

        $token = $this->request->bearerToken();

        if (empty($token)) {
            return null;
        }

        // Vérifier le JWT via un service externe
        $payload = $this->verifyToken($token);

        if (is_null($payload)) {
            return null;
        }

        $this->payload = $payload;

        $this->user = $this->provider->retrieveById($payload['sub']);

        return $this->user;
    }

    public function validate(array $credentials = []): bool
    {
        if (empty($credentials['token'])) {
            return false;
        }

        return ! is_null($this->verifyToken($credentials['token']));
    }

    /**
     * Vérification du JWT et récupération du payload
     */
    protected function verifyToken(string $token): ?array
    {
        $response = Http::withToken($token)
            ->get(config('services.auth.verify_url'));

        if (! $response->successful()) {
            return null;
        }

        return $response->json();
    }

    /**
     * Retourne le payload JWT vérifié
     */
    public function payload(): ?array
    {
        return $this->payload;
    }
}
Enregistrement dans AppServiceProvider :
Auth::extend('jwt', function (Application $app, string $name, array $config) {
    return new \App\Auth\JwtGuard(
        Auth::createUserProvider($config['provider'] ?? 'users'),
        $app->make('request'),
    );
});
Vous pouvez accéder à des méthodes propres au guard personnalisé comme Auth::guard('jwt')->payload(). Auth::guard() renvoie l’instance du guard elle-même, donc les méthodes hors interface sont également invocables.

Tests

Pour les tests unitaires d’un guard personnalisé, moquez le UserProvider pour vérifier le comportement du guard.
<?php

namespace Tests\Unit\Auth;

use App\Auth\ApiTokenGuard;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;

class ApiTokenGuardTest extends TestCase
{
    private UserProvider&MockObject $provider;
    private ApiTokenGuard $guard;

    protected function setUp(): void
    {
        parent::setUp();

        $this->provider = $this->createMock(UserProvider::class);
    }

    public function test_user_returns_null_when_no_token(): void
    {
        $request = Request::create('/');
        $guard   = new ApiTokenGuard($this->provider, $request);

        $this->assertNull($guard->user());
    }

    public function test_user_returns_user_with_valid_bearer_token(): void
    {
        $mockUser = $this->createMock(Authenticatable::class);

        $this->provider
            ->expects($this->once())
            ->method('retrieveByCredentials')
            ->with(['api_token' => 'valid-token'])
            ->willReturn($mockUser);

        $request = Request::create('/');
        $request->headers->set('Authorization', 'Bearer valid-token');

        $guard = new ApiTokenGuard($this->provider, $request);

        $this->assertSame($mockUser, $guard->user());
    }

    public function test_check_returns_false_when_no_token(): void
    {
        $request = Request::create('/');
        $guard   = new ApiTokenGuard($this->provider, $request);

        $this->assertFalse($guard->check());
    }

    public function test_validate_returns_false_without_api_token_credential(): void
    {
        $request = Request::create('/');
        $guard   = new ApiTokenGuard($this->provider, $request);

        $this->assertFalse($guard->validate([]));
    }
}
Pour les tests fonctionnels avec ActingAs, vous pouvez définir un utilisateur sur un guard spécifique.
// Authentifier un utilisateur sur un guard spécifique pour le test
$this->actingAs($user, 'api')
    ->getJson('/api/user')
    ->assertOk();

Pages associées

Authentification (introduction)

Vérifiez les starter kits et les flux d’authentification standards.

Conteneur de services

Comprenez le fonctionnement du conteneur de services utilisé lors de l’enregistrement d’un guard.
Dernière modification le 13 juillet 2026