Learn to build custom middleware in Laravel to enforce resource ownership. Secure your API routes by verifying user access before controllers ever execute.
Previously in this course, we explored handling API validation and form requests to ensure incoming data integrity. While validation ensures the shape of your data is correct, it doesn't guarantee the user has the right to touch that data.
In a multi-user project board, it’s not enough to know that a project_id exists in the database. You must ensure the authenticated user actually owns that project. Relying on controllers to perform these checks leads to repetitive, error-prone code. Today, we’ll move this logic into custom middleware, enforcing security at the route level to keep our controllers lean and secure.
Middleware provides a layer of "pre-flight" checks for your HTTP requests. By the time a request hits your controller, it should already be authenticated and authorized.
If you scatter ownership checks inside your controller methods, you violate the "Don't Repeat Yourself" (DRY) principle. If you decide to change how project ownership is calculated later, you'll have to hunt down every instance in your codebase. Middleware centralizes this logic, making your API more robust and easier to audit.
We want to ensure that if a route contains a {project} parameter, the authenticated user is the owner of that project. Let's create a piece of middleware called EnsureProjectOwner.
Run the following Artisan command:
Bashphp artisan make:middleware EnsureProjectOwner
This creates app/Http/Middleware/EnsureProjectOwner.php. We will inject the Request object and use it to verify the relationship between the authenticated user and the project model bound to the route.
PHPnamespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class EnsureProjectOwner { public function handle(Request $request, Closure $next): Response { #6A9955">// Get the project from the route parameters $project = $request->route('project'); #6A9955">// Check if the user is authorized to access this project if ($project && $project->user_id !== $request->user()->id) { return response()->json([ 'message' => 'You do not have permission to access this project.' ], 403); } return $next($request); } }
To use this, you must register it in bootstrap/app.php (for Laravel 11+) or app/Http/Kernel.php (for older versions). In a modern Laravel application, you add it to your web or API middleware aliases:
PHP#6A9955">// bootstrap/app.php ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'ensure.owner' => \App\Http\Middleware\EnsureProjectOwner::class, ]); })
Now, apply it to your routes in routes/api.php:
PHPRoute::middleware(['auth:sanctum', 'ensure.owner'])->group(function () { Route::get('/projects/{project}', [ProjectController::class, 'show']); Route::put('/projects/{project}', [ProjectController::class, 'update']); });
In the example above, we return a 403 Forbidden status code. This is the correct HTTP response for an authenticated user who is trying to access a resource they don't own.
Pro-tip: Never return a 404 Not Found for authorization failures unless you want to hide the existence of a resource entirely for security reasons (e.g., preventing ID enumeration). In most internal APIs, a 403 is much more helpful for debugging.
ProjectController from the service-oriented task management lesson.if ($project->user_id !== auth()->id()) checks currently inside your controller methods.ensure.owner middleware.GET a project belonging to User B. Ensure you receive a 403 response.{project} but you apply it to a route that doesn't have one, $request->route('project') will return null. Always check if the model exists before comparing IDs.auth:sanctum before ensure.owner. If you try to check ownership before the user is authenticated, $request->user() will be null, and your app will throw an exception.We’ve successfully moved our authorization logic out of the controller and into a dedicated middleware layer. This keeps our controllers focused on handling requests and returning responses, rather than policing data access. We’ve enforced security by verifying project ownership, ensuring that users only interact with data that belongs to them.
Up next: We will discuss Database Transactions for Data Integrity, ensuring that our multi-step operations remain atomic and consistent even when things go wrong.
Learn how to use Laravel middleware to secure your routes. We'll cover applying the auth middleware, protecting route groups, and managing redirects.
Read moreLearn how to perform a final production audit for your Task Manager. We cover clearing secrets, verifying dependencies, and running final tests before launch.
Implementing Middleware for API Security
Job Chaining and Batching
Feature Testing Fundamentals
Mocking Services and Repositories in Tests
Testing Events and Jobs
Database Factories and Seeding
API Versioning Strategies
Advanced Request Filtering and Sorting
Handling File Uploads in REST APIs
Real-time Notifications with Broadcasting
Using Observers for Model Lifecycle Hooks
Implementing Policies for Authorization
Customizing Authentication Guards
Rate Limiting API Endpoints
Eloquent Performance Optimization
Caching Strategies for Performance
Using Traits for Code Reuse
Advanced Dependency Injection with Service Providers
Command Line Tools with Artisan
Scheduled Tasks and Cron Jobs
Integrating Third-Party Services
Handling Webhooks
Logging and Monitoring
Database Migrations Best Practices
Advanced Testing: Integration Tests
Testing API Authentication
Code Quality and Static Analysis
Project Structure for Large Applications
Environment and Configuration Management
Deploying Laravel Applications
Database Indexing Strategies
Using Value Objects
Strategy Pattern for Business Rules
Advanced Queue Monitoring
Building a Search API
Handling Concurrency and Race Conditions
API Documentation with OpenAPI
Testing with Test Doubles
Implementing Multi-Tenancy
Refactoring Legacy Code
Using Middleware for Feature Flags
Building Reusable Packages
Performance Profiling
Secure API Design
Event Sourcing Concepts