Learn to implement Laravel policies to centralize and simplify your authorization logic. Secure your project board resources with clean, reusable access rules.
Previously in this course, we explored Implementing Middleware for API Security in Laravel to verify user ownership at the route level. While middleware is excellent for broad access control, it often leads to code duplication when your authorization logic grows complex. Today, we’ll move beyond middleware by implementing policies, a dedicated layer for granular, domain-specific authorization.
In our project board application, we need to decide if a user can update a task, delete a project, or add a team member. If we keep this logic inside controllers, they become bloated, hard to test, and difficult to maintain.
Laravel policies allow us to encapsulate authorization logic for specific Eloquent models into dedicated classes. This is the "Goldilocks" zone of security: it’s more flexible than simple middleware and more readable than scattered if statements throughout your service layer.
To start, generate a policy for our Task model using Artisan:
Bashphp artisan make:policy TaskPolicy --model=Task
This creates app/Policies/TaskPolicy.php. Let's implement a rule that only the task creator or a project manager can update a task.
PHPnamespace App\Policies; use App\Models\Task; use App\Models\User; class TaskPolicy { public function update(User $user, Task $task): bool { #6A9955">// A user can update if they own the task #6A9955">// or if they are the project owner return $user->id === $task->user_id || $user->id === $task->project->user_id; } }
Laravel automatically discovers this policy because it follows the naming convention (Task -> TaskPolicy). You don't need to manually register it unless you are using a custom structure.
With the policy defined, we no longer need to manually check if ($user->id !== $task->user_id) in our controller. Instead, we use the authorize method provided by the AuthorizesRequests trait (included by default in base controllers).
Refactoring our TaskController becomes trivial:
PHPpublic function update(Request $request, Task $task) { #6A9955">// Throws an AuthorizationException if the policy returns false $this->authorize('update', $task); $this->taskService->updateTask($task, $request->validated()); return response()->json(['message' => 'Task updated successfully']); }
If the user isn't authorized, Laravel automatically throws an Illuminate\Auth\Access\AuthorizationException, which we handle globally as discussed in our lesson on Mastering Laravel Exception Handling.
ProjectPolicy for your Project model.delete method in that policy. A project should only be deletable by the user who created it.$this->authorize('delete', $project) call inside your ProjectController@destroy method.403 Forbidden response.$this->authorize('update', $task)). Use the class name only for "create" actions (e.g., $this->authorize('create', Task::class)).$user can be null if you allow guest access to certain endpoints. Always type-hint ?User if your policy needs to handle unauthenticated users.We’ve moved authorization out of our controllers and into dedicated, testable policy classes. By leveraging authorize(), we keep our controllers thin and our security logic centralized. This approach makes our project board significantly more maintainable as we add features like team roles and permissions.
Up next: We'll dive into Customizing Authentication Guards to support multi-auth scenarios, such as separating API tokens from web-based sessions.
Master OAuth2 implementation in Laravel by building a secure Authorization Code flow. Learn to handle token issuance, validation, and architectural best practices.
Read moreLearn to secure your Laravel API by mastering CORS, implementing CSRF protection for SPAs, and managing sensitive headers to prevent common web attacks.
Implementing Policies for Authorization