DEV Community

Cover image for Laravel Guards: A Comprehensive Guide
alamriku
alamriku

Posted on

Laravel Guards: A Comprehensive Guide

Table of Contents

  1. Introduction to Guards
  2. Understanding Authentication vs Authorization
  3. Types of Guards
  4. How Guards Work
  5. Creating Custom Guards
  6. Practical Examples
  7. Best Practices

Introduction to Guards {#introduction}

What are Guards?

Think of Guards as security checkpoints at different entrances to your application. Like a building with multiple entrances, your Laravel app can have different ways for users to authenticate:

Your App Building: [Web Browser Door] 🚪 -- Session Guard (cookie-based) | [Phone App Door] 📱 -- Token Guard (API token) | [API Door] 🔌 -- Sanctum Guard (modern tokens) | [Admin Door] 👮 -- Custom Guard (special access) 
Enter fullscreen mode Exit fullscreen mode

Why Use Guards?

  • Different authentication methods for different client types
  • Separate user types (customers, admins, vendors)
  • Multiple authentication systems in one app
  • Enhanced security through isolation

Understanding Authentication vs Authorization {#auth-vs-authz}

Authentication = "Who are you?" 🔐

// Guards handle authentication Auth::check(); // Are you logged in? Auth::user(); // Who are you? Auth::id(); // What's your ID? 
Enter fullscreen mode Exit fullscreen mode

Authorization = "What can you do?" 🚦

// Gates and Policies handle authorization Gate::allows('edit-post', $post); // Can you edit this? $user->can('delete', $post); // Can user delete this? $this->authorize('update', $resource); // Throw error if not allowed 
Enter fullscreen mode Exit fullscreen mode

Types of Guards {#types-of-guards}

1. Session Guard (Default Web Guard)

'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ], 
Enter fullscreen mode Exit fullscreen mode

How it works:

  • User logs in with credentials
  • Laravel creates a session and stores user ID
  • Session ID saved in cookie
  • Subsequent requests use cookie to identify user

Use cases:

  • Traditional web applications
  • Server-side rendered pages
  • Applications requiring "remember me" functionality

2. Token Guard (API Authentication)

'guards' => [ 'api' => [ 'driver' => 'token', 'provider' => 'users', ], ], 
Enter fullscreen mode Exit fullscreen mode

How it works:

  • User receives a token after authentication
  • Token sent with each API request
  • No session or cookies required

Use cases:

  • Mobile applications
  • Third-party API access
  • Stateless authentication

3. Sanctum Guard (Modern API/SPA)

'guards' => [ 'sanctum' => [ 'driver' => 'sanctum', 'provider' => 'users', ], ], 
Enter fullscreen mode Exit fullscreen mode

Features:

  • Cookie-based authentication for SPAs
  • Token-based authentication for mobile apps
  • Built-in CSRF protection
  • API token abilities/scopes

4. Passport Guard (OAuth2 Server)

'guards' => [ 'passport' => [ 'driver' => 'passport', 'provider' => 'users', ], ], 
Enter fullscreen mode Exit fullscreen mode

Features:

  • Full OAuth2 server implementation
  • Personal access tokens
  • Password grant tokens
  • Authorization codes

How Guards Work {#how-guards-work}

The Authentication Flow

// 1. User attempts login $credentials = ['email' => 'user@example.com', 'password' => 'password']; // 2. Laravel validates credentials if (Auth::attempt($credentials)) { // 3. Create session/token // 4. Store user identification } // 5. Subsequent requests $user = Auth::user(); // Retrieves authenticated user 
Enter fullscreen mode Exit fullscreen mode

Behind the Scenes

// How Auth::user() works internally public function user() { // Check if already loaded for this request if (!is_null($this->user)) { return $this->user; } // Get user ID from session $id = $this->session->get($this->getName()); // Load user from database if (!is_null($id)) { $this->user = $this->provider->retrieveById($id); } return $this->user; } 
Enter fullscreen mode Exit fullscreen mode

Guard Configuration

// config/auth.php return [ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], ], ]; 
Enter fullscreen mode Exit fullscreen mode

Creating Custom Guards {#custom-guards}

Step 1: Create the Guard Class

<?php namespace App\Auth; use Illuminate\Http\Request; use Illuminate\Auth\GuardHelpers; use Illuminate\Contracts\Auth\Guard; use Illuminate\Contracts\Auth\UserProvider; class MarketingGuard implements Guard { use GuardHelpers; protected $request; protected $provider; protected $tokenKey = 'X-Marketing-Token'; public function __construct(UserProvider $provider, Request $request) { $this->provider = $provider; $this->request = $request; } public function user() { if (!is_null($this->user)) { return $this->user; } $token = $this->getTokenFromRequest(); if (!empty($token)) { $this->user = $this->provider->retrieveByCredentials([ 'api_token' => $token ]); } return $this->user; } protected function getTokenFromRequest() { $token = $this->request->header($this->tokenKey); if (str_starts_with($token, 'Bearer ')) { return substr($token, 7); } return $token; } public function validate(array $credentials = []) { if (empty($credentials['api_token'])) { return false; } $user = $this->provider->retrieveByCredentials($credentials); return !is_null($user); } } 
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Custom Provider

<?php namespace App\Auth; use Illuminate\Auth\EloquentUserProvider; use Illuminate\Contracts\Auth\Authenticatable; class MarketingUserProvider extends EloquentUserProvider { public function retrieveByCredentials(array $credentials) { if (empty($credentials['api_token'])) { return null; } return $this->createModel() ->where('api_token', $credentials['api_token']) ->where('is_active', true) ->first(); } public function validateCredentials(Authenticatable $user, array $credentials) { return $user->api_token === $credentials['api_token']; } } 
Enter fullscreen mode Exit fullscreen mode

Step 3: Register Guard and Provider

// app/Providers/AuthServiceProvider.php public function boot() { // Register custom guard Auth::extend('marketing', function ($app, $name, array $config) { $provider = Auth::createUserProvider($config['provider']); $request = $app->make('request'); return new MarketingGuard($provider, $request); }); // Register custom provider Auth::provider('marketing', function ($app, array $config) { return new MarketingUserProvider( $app['hash'], $config['model'] ); }); } 
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure in auth.php

// config/auth.php 'guards' => [ 'marketing' => [ 'driver' => 'marketing', 'provider' => 'marketing_users', ], ], 'providers' => [ 'marketing_users' => [ 'driver' => 'marketing', 'model' => App\Models\MarketingUser::class, ], ], 
Enter fullscreen mode Exit fullscreen mode

IMPORTANT: Name Matching Rules

When creating custom guards, certain names MUST match exactly:

// 1. Guard Driver Names 'guards' => [ 'marketing' => [ 'driver' => 'marketing', // ← This name... 'provider' => 'marketing_users', ], ], // Must match this: Auth::extend('marketing', function ($app, $name, array $config) { // ↑ This name must match the driver name above }); // 2. Provider Driver Names 'providers' => [ 'marketing_users' => [ 'driver' => 'marketing', // ← This name... 'model' => App\Models\MarketingUser::class, ], ], // Must match this: Auth::provider('marketing', function ($app, array $config) { // ↑ This name must match the provider driver name above }); // 3. Guard Names When Using Auth::guard('marketing')->user(); // ← Must match guard name in config // 4. Provider References 'guards' => [ 'marketing' => [ 'driver' => 'marketing', 'provider' => 'marketing_users', // ← This name... ], ], 'providers' => [ 'marketing_users' => [ // ← Must match this provider name 'driver' => 'marketing', 'model' => App\Models\MarketingUser::class, ], ], 
Enter fullscreen mode Exit fullscreen mode

Naming Freedom

You have complete freedom with:

  • Middleware names in Kernel.php
  • Route middleware names
  • Class names
  • File names
// app/Http/Kernel.php - These can be anything protected $routeMiddleware = [ 'auth.marketing' => \App\Http\Middleware\AuthenticateMarketing::class, 'marketing' => \App\Http\Middleware\AuthenticateMarketing::class, 'mkt.auth' => \App\Http\Middleware\AuthenticateMarketing::class, 'check.marketing' => \App\Http\Middleware\AuthenticateMarketing::class, ]; 
Enter fullscreen mode Exit fullscreen mode

Complete Example with Name Matching

// config/auth.php 'guards' => [ 'custom_auth' => [ // Guard name (your choice) 'driver' => 'my_driver', // Must match Auth::extend() 'provider' => 'my_users', // Must match a provider below ], ], 'providers' => [ 'my_users' => [ // Provider name (must match above) 'driver' => 'my_provider', // Must match Auth::provider() 'model' => User::class, ], ], // AuthServiceProvider.php Auth::extend('my_driver', function () { // Must match driver in guard // Return guard instance }); Auth::provider('my_provider', function () { // Must match driver in provider // Return provider instance }); // Using the guard Auth::guard('custom_auth')->user(); // Must use exact guard name // Middleware (can be named anything) 'my.auth' => MyAuthMiddleware::class, // Your choice of name 
Enter fullscreen mode Exit fullscreen mode

Step 5: Create Middleware

<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Auth; class AuthenticateMarketing { public function handle($request, Closure $next) { if (!Auth::guard('marketing')->check()) { return response()->json(['error' => 'Unauthorized'], 401); } Auth::shouldUse('marketing'); return $next($request); } } 
Enter fullscreen mode Exit fullscreen mode

Practical Examples {#practical-examples}

Multi-Guard Application

// E-commerce application with multiple user types // config/auth.php 'guards' => [ 'customer' => [ 'driver' => 'session', 'provider' => 'customers', ], 'vendor' => [ 'driver' => 'sanctum', 'provider' => 'vendors', ], 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'delivery' => [ 'driver' => 'token', 'provider' => 'drivers', ], ], // Routes for different user types // routes/customer.php Route::middleware(['auth:customer'])->group(function () { Route::get('/orders', [CustomerOrderController::class, 'index']); Route::get('/profile', [CustomerProfileController::class, 'show']); }); // routes/vendor.php Route::middleware(['auth:vendor'])->group(function () { Route::get('/products', [VendorProductController::class, 'index']); Route::post('/inventory', [VendorInventoryController::class, 'update']); }); // routes/admin.php Route::middleware(['auth:admin'])->group(function () { Route::get('/dashboard', [AdminDashboardController::class, 'index']); Route::get('/users', [AdminUserController::class, 'index']); }); // routes/api.php Route::middleware(['auth:delivery'])->prefix('driver')->group(function () { Route::get('/orders', [DriverOrderController::class, 'index']); Route::post('/status', [DriverStatusController::class, 'update']); }); 
Enter fullscreen mode Exit fullscreen mode

Smart Guard Detection

// Middleware that detects which guard to use class SmartAuthMiddleware { public function handle($request, $next) { // Check for API token if ($request->bearerToken()) { Auth::shouldUse('api'); } // Check for vendor header elseif ($request->header('X-Vendor-Token')) { Auth::shouldUse('vendor'); } // Check for session cookie elseif ($request->hasCookie('laravel_session')) { Auth::shouldUse('web'); } return $next($request); } } 
Enter fullscreen mode Exit fullscreen mode

Using Multiple Guards in Controllers

class ProfileController extends Controller { public function show(Request $request) { // Get current guard $guardName = Auth::getDefaultDriver(); // Different responses for different guards switch ($guardName) { case 'web': return view('profile.web', ['user' => Auth::user()]); case 'api': return response()->json(['user' => Auth::user()]); case 'vendor': return response()->json([ 'vendor' => Auth::user(), 'products' => Auth::user()->products ]); default: return response('Unknown client', 400); } } } 
Enter fullscreen mode Exit fullscreen mode

Best Practices {#best-practices}

1. Use Appropriate Guards for Use Cases

// Web browsers - use session guard 'customer' => ['driver' => 'session'], // Mobile apps - use sanctum/token 'mobile' => ['driver' => 'sanctum'], // Third-party APIs - use passport/custom 'external' => ['driver' => 'passport'], 
Enter fullscreen mode Exit fullscreen mode

2. Consistent Naming Conventions

// Good - follows pattern 'guards' => [ 'customer' => [...], 'customer_api' => [...], ], 'providers' => [ 'customers' => [...], 'customer_api_users' => [...], ], // Bad - inconsistent 'guards' => [ 'user' => [...], 'apiCustomer' => [...], ], 
Enter fullscreen mode Exit fullscreen mode

3. Separate Routes by Guard Type

// routes/web.php - Session-based Route::middleware(['auth:web'])->group(function () { // Web routes }); // routes/api.php - Token-based Route::middleware(['auth:api'])->group(function () { // API routes }); // routes/admin.php - Admin area Route::middleware(['auth:admin'])->group(function () { // Admin routes }); 
Enter fullscreen mode Exit fullscreen mode

4. Use Guard-Specific Middleware

// Create specific middleware for each guard class EnsureCustomerAuthenticated { public function handle($request, $next) { if (!Auth::guard('customer')->check()) { return redirect('/customer/login'); } return $next($request); } } 
Enter fullscreen mode Exit fullscreen mode

5. Handle Guard-Specific Login Pages

// app/Exceptions/Handler.php protected function unauthenticated($request, AuthenticationException $exception) { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); } // Redirect to appropriate login page based on guard $guard = $exception->guards()[0] ?? null; switch ($guard) { case 'admin': return redirect('/admin/login'); case 'vendor': return redirect('/vendor/login'); default: return redirect('/login'); } } 
Enter fullscreen mode Exit fullscreen mode

6. Security Considerations

// Always validate tokens properly public function validateToken($token) { // Check token format if (!preg_match('/^[a-zA-Z0-9]{64}$/', $token)) { return false; } // Check token expiry $user = User::where('api_token', $token) ->where('token_expires_at', '>', now()) ->first(); return $user !== null; } // Use proper session configuration // config/session.php 'lifetime' => 120, 'expire_on_close' => false, 'encrypt' => true, 'secure' => true, // HTTPS only 'same_site' => 'lax', 
Enter fullscreen mode Exit fullscreen mode

Common Patterns and Solutions

Pattern 1: Multi-tenant Application

// Different guards for different tenant types 'guards' => [ 'tenant_a' => [ 'driver' => 'session', 'provider' => 'tenant_a_users', ], 'tenant_b' => [ 'driver' => 'session', 'provider' => 'tenant_b_users', ], ], // Route middleware Route::domain('{tenant}.app.com')->group(function () { Route::middleware(['auth:tenant_' . request()->tenant])->group(function () { // Tenant-specific routes }); }); 
Enter fullscreen mode Exit fullscreen mode

Pattern 2: API Versioning with Guards

// Different guards for API versions 'guards' => [ 'api_v1' => [ 'driver' => 'token', 'provider' => 'users', ], 'api_v2' => [ 'driver' => 'sanctum', 'provider' => 'users', ], ], // Versioned routes Route::prefix('v1')->middleware(['auth:api_v1'])->group(function () { // V1 API routes }); Route::prefix('v2')->middleware(['auth:api_v2'])->group(function () { // V2 API routes }); 
Enter fullscreen mode Exit fullscreen mode

Pattern 3: Social Authentication

// Custom social guard class SocialGuard implements Guard { public function user() { $token = $this->request->bearerToken(); // Verify with social provider $socialUser = $this->verifySocialToken($token); // Find or create local user return User::firstOrCreate([ 'email' => $socialUser->email, ], [ 'name' => $socialUser->name, 'provider' => $socialUser->provider, ]); } } 
Enter fullscreen mode Exit fullscreen mode

Debugging Guards

Common Issues and Solutions

// Debug current guard dd(Auth::getDefaultDriver()); // Check if user is authenticated if (Auth::guard('custom')->check()) { dd(Auth::guard('custom')->user()); } // Debug authentication failures try { $user = Auth::guard('api')->user(); } catch (\Exception $e) { Log::error('Auth failed: ' . $e->getMessage()); } // Test specific guard Route::get('/test-guard/{guard}', function ($guard) { return [ 'guard' => $guard, 'authenticated' => Auth::guard($guard)->check(), 'user' => Auth::guard($guard)->user(), ]; }); 
Enter fullscreen mode Exit fullscreen mode

Summary

Laravel Guards provide a flexible authentication system that allows you to:

  1. Support multiple authentication methods simultaneously
  2. Separate different user types with different authentication needs
  3. Implement custom authentication logic for special requirements
  4. Maintain security through isolation of authentication systems

Key takeaways:

  • Guards define HOW users authenticate (session, token, etc.)
  • Providers define WHERE user data comes from
  • Middleware connects routes to specific guards
  • Multiple guards can coexist in the same application
  • Custom guards extend Laravel's authentication capabilities

By understanding and properly implementing guards, you can build secure, flexible authentication systems that meet diverse application requirements.

Top comments (0)