Laravel cookbook · Beginner

Authorize actions with policies

Permission checks are scattered across controllers and views and drift apart.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Put model-related rules in a policy, link it to the model, and use authorize() / can middleware in routes and @can in Blade. Use before() for super-admins.

Solution

<?php
// app/Policies/ProjectPolicy.php
namespace App\Policies;

use App\Models\Project;
use App\Models\User;
use Illuminate\Auth\Access\Response;

class ProjectPolicy
{
    public function before(User $user, string $ability): ?bool
    {
        return $user->is_admin ? true : null;
    }

    public function view(User $user, Project $project): bool
    {
        return $project->team->members()->whereKey($user->id)->exists();
    }

    public function update(User $user, Project $project): Response
    {
        return $project->owner_id === $user->id
            ? Response::allow()
            : Response::denyAsNotFound();           // 404 instead of revealing the project exists
    }
}
<?php
// routes/web.php
use App\Http\Controllers\ProjectController;
use Illuminate\Support\Facades\Route;

Route::put('/projects/{project}', [ProjectController::class, 'update'])->can('update', 'project');

// In a controller method
// Gate::authorize('view', $project);

Pitfalls

  • Checking permissions only in Blade (hiding buttons) and not on the routes.
  • Querying the database repeatedly in policies called inside loops; eager load what the policy needs.
  • Returning 403 for resources the user shouldn't know exist; use denyAsNotFound().

Variations

  • Add #[UsePolicy(ProjectPolicy::class)] on the model when the policy isn't auto-discovered.
  • For role-based permissions across the app, spatie/laravel-permission integrates with gates.

Further reading: Authorization: policies

Reference: https://laravel.com/docs/13.x/authorization#creating-policies

More Laravel recipes

Was this page helpful?