Middleware and Authentication
Middleware provides a convenient mechanism for filtering HTTP requests entering your application. Think of middleware as layers that HTTP requests mu
Middleware provides a convenient mechanism for filtering HTTP requests entering your application. Think of middleware as layers that HTTP requests must pass through before reaching your application's core logic. Each middleware can inspect, modify, or reject requests.
Use Cases:
1HTTP Request
2 ↓
3Global Middleware
4 ↓
5Route Middleware
6 ↓
7Controller
8 ↓
9Response
10 ↓
11Middleware (on response)
12 ↓
13HTTP ResponseBefore Middleware: Execute before the request reaches the controller
1namespace App\Http\Middleware;
2
3use Closure;
4use Illuminate\Http\Request;
5
6class BeforeMiddleware
7{
8 public function handle(Request $request, Closure $next)
9 {
10 // Perform action BEFORE request
11 if ($request->input('blocked')) {
12 abort(403, 'Access denied');
13 }
14
15 return $next($request);
16 }
17}After Middleware: Execute after the controller processes the request
1namespace App\Http\Middleware;
2
3use Closure;
4use Illuminate\Http\Request;
5use Symfony\Component\HttpFoundation\Response;
6
7class AfterMiddleware
8{
9 public function handle(Request $request, Closure $next): Response
10 {
11 $response = $next($request);
12
13 // Perform action AFTER request
14 $response->header('X-Custom-Header', 'value');
15
16 return $response;
17 }
18}1# Create middleware
2php artisan make:middleware CheckAge
3
4# Create middleware in a subdirectory
5php artisan make:middleware Admin/EnsureAdmin1namespace App\Http\Middleware;
2
3use Closure;
4use Illuminate\Http\Request;
5use Symfony\Component\HttpFoundation\Response;
6
7class CheckAge
8{
9 /**
10 * Handle an incoming request.
11 */
12 public function handle(Request $request, Closure $next): Response
13 {
14 if ($request->age < 18) {
15 return redirect('home')->with('error', 'You must be 18 or older');
16 }
17
18 return $next($request);
19 }
20}1namespace App\Http\Middleware;
2
3use Closure;
4use Illuminate\Http\Request;
5use Symfony\Component\HttpFoundation\Response;
6
7class CheckRole
8{
9 /**
10 * Handle an incoming request.
11 *
12 * @param string ...$roles
13 */
14 public function handle(Request $request, Closure $next, string ...$roles): Response
15 {
16 if (!$request->user() || !$request->user()->hasAnyRole($roles)) {
17 abort(403, 'Unauthorized action.');
18 }
19
20 return $next($request);
21 }
22}Global Middleware (app/Http/Kernel.php or bootstrap/app.php in Laravel 11):
1// Laravel 11 - bootstrap/app.php
2use App\Http\Middleware\CheckAge;
3
4return Application::configure(basePath: dirname(__DIR__))
5 ->withMiddleware(function (Middleware $middleware) {
6 $middleware->append(CheckAge::class);
7 })
8 ->create();
9
10// Laravel 10 and earlier - app/Http/Kernel.php
11protected $middleware = [
12 \App\Http\Middleware\TrustProxies::class,
13 \App\Http\Middleware\CheckAge::class,
14];Route Middleware:
1// Laravel 11 - bootstrap/app.php
2->withMiddleware(function (Middleware $middleware) {
3 $middleware->alias([
4 'check.age' => \App\Http\Middleware\CheckAge::class,
5 'role' => \App\Http\Middleware\CheckRole::class,
6 ]);
7})
8
9// Laravel 10 - app/Http/Kernel.php
10protected $middlewareAliases = [
11 'check.age' => \App\Http\Middleware\CheckAge::class,
12 'role' => \App\Http\Middleware\CheckRole::class,
13];1use App\Http\Controllers\UserController;
2
3// Single middleware
4Route::get('/profile', [UserController::class, 'show'])
5 ->middleware('auth');
6
7// Multiple middleware
8Route::get('/admin', [UserController::class, 'admin'])
9 ->middleware(['auth', 'role:admin']);
10
11// Middleware with parameters
12Route::get('/moderator', function () {
13 // ...
14})->middleware('role:moderator,admin');
15
16// Exclude middleware
17Route::get('/public', function () {
18 // ...
19})->withoutMiddleware(['auth']);1namespace App\Http\Controllers;
2
3use Illuminate\Http\Request;
4
5class UserController extends Controller
6{
7 public function __construct()
8 {
9 // Apply to all methods
10 $this->middleware('auth');
11
12 // Apply to specific methods
13 $this->middleware('role:admin')->only(['destroy', 'edit']);
14
15 // Exclude from specific methods
16 $this->middleware('auth')->except(['index', 'show']);
17
18 // Closure middleware
19 $this->middleware(function (Request $request, $next) {
20 if ($request->user()->banned) {
21 abort(403, 'Your account has been banned');
22 }
23 return $next($request);
24 });
25 }
26}1// Apply middleware to group
2Route::middleware(['auth', 'verified'])->group(function () {
3 Route::get('/dashboard', [DashboardController::class, 'index']);
4 Route::get('/profile', [ProfileController::class, 'edit']);
5 Route::post('/profile', [ProfileController::class, 'update']);
6});
7
8// Nested groups
9Route::middleware('auth')->group(function () {
10 Route::prefix('admin')->middleware('role:admin')->group(function () {
11 Route::get('/users', [AdminController::class, 'users']);
12 Route::get('/settings', [AdminController::class, 'settings']);
13 });
14});1// Require authentication
2Route::get('/dashboard', function () {
3 // Only authenticated users
4})->middleware('auth');
5
6// Require authentication for specific guard
7Route::get('/admin', function () {
8 // ...
9})->middleware('auth:admin');
10
11// Redirect guests to specific route
12Route::get('/profile', function () {
13 // ...
14})->middleware('auth')->name('profile');1// Only allow guests (unauthenticated users)
2Route::middleware('guest')->group(function () {
3 Route::get('/login', [AuthController::class, 'showLogin']);
4 Route::post('/login', [AuthController::class, 'login']);
5 Route::get('/register', [AuthController::class, 'showRegister']);
6});1// Require email verification
2Route::middleware(['auth', 'verified'])->group(function () {
3 Route::get('/dashboard', [DashboardController::class, 'index']);
4});1// Rate limiting: 60 requests per minute
2Route::middleware('throttle:60,1')->group(function () {
3 Route::get('/api/users', [ApiController::class, 'users']);
4});
5
6// Named rate limiter
7Route::middleware('throttle:api')->group(function () {
8 // ...
9});
10
11// Configure in RouteServiceProvider
12use Illuminate\Cache\RateLimiting\Limit;
13use Illuminate\Support\Facades\RateLimiter;
14
15RateLimiter::for('api', function (Request $request) {
16 return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
17});Laravel Breeze (Simple):
1composer require laravel/breeze --dev
2php artisan breeze:install
3
4# Options:
5# - blade (traditional server-side)
6# - react (with Inertia.js)
7# - vue (with Inertia.js)
8# - api (API-only with Sanctum)
9
10npm install
11npm run dev
12php artisan migrateLaravel Sanctum (API Authentication):
1composer require laravel/sanctum
2php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
3php artisan migrateconfig/auth.php:
1return [
2 'defaults' => [
3 'guard' => 'web',
4 'passwords' => 'users',
5 ],
6
7 'guards' => [
8 'web' => [
9 'driver' => 'session',
10 'provider' => 'users',
11 ],
12
13 'api' => [
14 'driver' => 'sanctum',
15 'provider' => 'users',
16 ],
17
18 'admin' => [
19 'driver' => 'session',
20 'provider' => 'admins',
21 ],
22 ],
23
24 'providers' => [
25 'users' => [
26 'driver' => 'eloquent',
27 'model' => App\Models\User::class,
28 ],
29
30 'admins' => [
31 'driver' => 'eloquent',
32 'model' => App\Models\Admin::class,
33 ],
34 ],
35
36 'passwords' => [
37 'users' => [
38 'provider' => 'users',
39 'table' => 'password_reset_tokens',
40 'expire' => 60,
41 'throttle' => 60,
42 ],
43 ],
44];1use Illuminate\Support\Facades\Auth;
2
3class LoginController extends Controller
4{
5 public function login(Request $request)
6 {
7 $credentials = $request->validate([
8 'email' => 'required|email',
9 'password' => 'required',
10 ]);
11
12 // Attempt to authenticate
13 if (Auth::attempt($credentials)) {
14 $request->session()->regenerate();
15
16 return redirect()->intended('dashboard');
17 }
18
19 return back()->withErrors([
20 'email' => 'The provided credentials do not match our records.',
21 ])->onlyInput('email');
22 }
23
24 public function logout(Request $request)
25 {
26 Auth::logout();
27
28 $request->session()->invalidate();
29 $request->session()->regenerateToken();
30
31 return redirect('/');
32 }
33}1// With "remember me" functionality
2if (Auth::attempt($credentials, $remember = true)) {
3 // User will be remembered
4}
5
6// Validate if user was "remembered"
7if (Auth::viaRemember()) {
8 // User was remembered
9}1// Use specific guard
2if (Auth::guard('admin')->attempt($credentials)) {
3 // Admin authenticated
4}
5
6// Get authenticated user from specific guard
7$admin = Auth::guard('admin')->user();
8
9// Check authentication on specific guard
10if (Auth::guard('admin')->check()) {
11 // Admin is authenticated
12}
13
14// Logout from specific guard
15Auth::guard('admin')->logout();1use Illuminate\Support\Facades\Auth;
2
3// Get authenticated user
4$user = Auth::user();
5$user = auth()->user();
6$user = $request->user();
7
8// Get user ID
9$id = Auth::id();
10
11// Check if authenticated
12if (Auth::check()) {
13 // User is authenticated
14}
15
16// Check if guest
17if (Auth::guest()) {
18 // User is not authenticated
19}Define authorization logic in App\Providers\AuthServiceProvider:
1use Illuminate\Support\Facades\Gate;
2
3public function boot(): void
4{
5 // Simple gate
6 Gate::define('update-post', function (User $user, Post $post) {
7 return $user->id === $post->user_id;
8 });
9
10 // Gate with optional user (for guests)
11 Gate::define('view-post', function (?User $user, Post $post) {
12 if ($post->published) {
13 return true;
14 }
15
16 return $user && $user->id === $post->user_id;
17 });
18
19 // Admin gate
20 Gate::define('admin', function (User $user) {
21 return $user->role === 'admin';
22 });
23
24 // Before hook (runs before all gates)
25 Gate::before(function (User $user, string $ability) {
26 if ($user->isAdmin()) {
27 return true; // Admins can do anything
28 }
29 });
30}Using Gates:
1use Illuminate\Support\Facades\Gate;
2
3// Check authorization
4if (Gate::allows('update-post', $post)) {
5 // User can update post
6}
7
8if (Gate::denies('update-post', $post)) {
9 // User cannot update post
10}
11
12// Authorize or abort (throws 403)
13Gate::authorize('update-post', $post);
14
15// Check for any ability
16if (Gate::any(['update-post', 'delete-post'], $post)) {
17 // User can update OR delete
18}
19
20// Check for all abilities
21if (Gate::all(['update-post', 'publish-post'], $post)) {
22 // User can update AND publish
23}
24
25// Via User model
26if ($request->user()->can('update-post', $post)) {
27 // ...
28}
29
30if ($request->user()->cannot('update-post', $post)) {
31 abort(403);
32}Create policy classes for model authorization:
1# Generate policy
2php artisan make:policy PostPolicy
3
4# Generate policy for model
5php artisan make:policy PostPolicy --model=PostPostPolicy.php:
1namespace App\Policies;
2
3use App\Models\Post;
4use App\Models\User;
5
6class PostPolicy
7{
8 /**
9 * Determine if the user can view any posts.
10 */
11 public function viewAny(User $user): bool
12 {
13 return true;
14 }
15
16 /**
17 * Determine if the user can view the post.
18 */
19 public function view(?User $user, Post $post): bool
20 {
21 return $post->published || ($user && $user->id === $post->user_id);
22 }
23
24 /**
25 * Determine if the user can create posts.
26 */
27 public function create(User $user): bool
28 {
29 return $user->email_verified_at !== null;
30 }
31
32 /**
33 * Determine if the user can update the post.
34 */
35 public function update(User $user, Post $post): bool
36 {
37 return $user->id === $post->user_id;
38 }
39
40 /**
41 * Determine if the user can delete the post.
42 */
43 public function delete(User $user, Post $post): bool
44 {
45 return $user->id === $post->user_id;
46 }
47
48 /**
49 * Determine if the user can restore the post.
50 */
51 public function restore(User $user, Post $post): bool
52 {
53 return $user->id === $post->user_id;
54 }
55
56 /**
57 * Determine if the user can permanently delete the post.
58 */
59 public function forceDelete(User $user, Post $post): bool
60 {
61 return $user->isAdmin();
62 }
63}Register Policy:
1// App\Providers\AuthServiceProvider
2protected $policies = [
3 Post::class => PostPolicy::class,
4];Using Policies:
1// In controller
2public function update(Request $request, Post $post)
3{
4 $this->authorize('update', $post);
5
6 // Update post...
7}
8
9// Check without aborting
10if ($request->user()->can('update', $post)) {
11 // User can update
12}
13
14// In Blade
15@can('update', $post)
16 <a href="{{ route('posts.edit', $post) }}">Edit</a>
17@endcan
18
19@cannot('delete', $post)
20 <p>You cannot delete this post</p>
21@endcannot1// app/Models/User.php
2use Laravel\Sanctum\HasApiTokens;
3
4class User extends Authenticatable
5{
6 use HasApiTokens, HasFactory, Notifiable;
7}
8
9// config/sanctum.php - configure token expiration
10'expiration' => 60 * 24, // 24 hours1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Hash;
3use Illuminate\Validation\ValidationException;
4
5class AuthController extends Controller
6{
7 public function login(Request $request)
8 {
9 $request->validate([
10 'email' => 'required|email',
11 'password' => 'required',
12 ]);
13
14 $user = User::where('email', $request->email)->first();
15
16 if (!$user || !Hash::check($request->password, $user->password)) {
17 throw ValidationException::withMessages([
18 'email' => ['The provided credentials are incorrect.'],
19 ]);
20 }
21
22 // Create token
23 $token = $user->createToken('api-token')->plainTextToken;
24
25 return response()->json([
26 'token' => $token,
27 'user' => $user,
28 ]);
29 }
30
31 public function logout(Request $request)
32 {
33 // Revoke current token
34 $request->user()->currentAccessToken()->delete();
35
36 return response()->json(['message' => 'Logged out']);
37 }
38
39 public function logoutAll(Request $request)
40 {
41 // Revoke all tokens
42 $request->user()->tokens()->delete();
43
44 return response()->json(['message' => 'Logged out from all devices']);
45 }
46}1// routes/api.php
2use Illuminate\Support\Facades\Route;
3
4Route::middleware('auth:sanctum')->group(function () {
5 Route::get('/user', function (Request $request) {
6 return $request->user();
7 });
8
9 Route::apiResource('posts', PostController::class);
10});
11
12// Public routes
13Route::post('/login', [AuthController::class, 'login']);
14Route::post('/register', [AuthController::class, 'register']);1// Create token with specific abilities
2$token = $user->createToken('api-token', ['post:create', 'post:read'])->plainTextToken;
3
4// Check abilities in middleware
5Route::middleware(['auth:sanctum', 'abilities:post:create,post:read'])->group(function () {
6 // ...
7});
8
9// Check in controller
10if ($request->user()->tokenCan('post:create')) {
11 // User's token has this ability
12}1<form method="POST">
2 @csrf
3 <!-- Form fields -->
4</form>1$request->session()->regenerate();1use Illuminate\Support\Facades\Hash;
2
3$user->password = Hash::make($request->password);1Route::post('/login', [LoginController::class, 'login'])
2 ->middleware('throttle:5,1'); // 5 attempts per minute1// In policy
2public function update(User $user, Post $post): bool
3{
4 return Cache::remember(
5 "user.{$user->id}.can-update-post.{$post->id}",
6 60,
7 fn() => $user->id === $post->user_id
8 );
9}1$posts = Post::with('user')->get();
2
3foreach ($posts as $post) {
4 if (Gate::allows('update', $post)) {
5 // No N+1 query issue
6 }
7}1// bootstrap/app.php
2->withMiddleware(function (Middleware $middleware) {
3 $middleware->group('admin', [
4 'auth',
5 'verified',
6 'role:admin',
7 ]);
8})
9
10// Usage
11Route::middleware('admin')->group(function () {
12 // Admin routes
13});Middleware and Authentication are fundamental to securing Laravel applications:
Proper middleware and authentication ensure your application is secure and only authorized users can access protected resources!
Your progress is saved in this browser, on this device only.