Table of Contents
- Introduction to Guards
- Understanding Authentication vs Authorization
- Types of Guards
- How Guards Work
- Creating Custom Guards
- Practical Examples
- 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)
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?
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
Types of Guards {#types-of-guards}
1. Session Guard (Default Web Guard)
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ],
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', ], ],
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', ], ],
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', ], ],
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
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; }
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, ], ], ];
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); } }
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']; } }
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'] ); }); }
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, ], ],
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, ], ],
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, ];
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
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); } }
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']); });
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); } }
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); } } }
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'],
2. Consistent Naming Conventions
// Good - follows pattern 'guards' => [ 'customer' => [...], 'customer_api' => [...], ], 'providers' => [ 'customers' => [...], 'customer_api_users' => [...], ], // Bad - inconsistent 'guards' => [ 'user' => [...], 'apiCustomer' => [...], ],
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 });
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); } }
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'); } }
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',
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 }); });
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 });
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, ]); } }
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(), ]; });
Summary
Laravel Guards provide a flexible authentication system that allows you to:
- Support multiple authentication methods simultaneously
- Separate different user types with different authentication needs
- Implement custom authentication logic for special requirements
- 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)