SyntaxStudy
Sign Up
Laravel Authorization with Policies and Gates
Laravel Beginner 1 min read

Authorization with Policies and Gates

Laravel's authorization layer separates authentication (who you are) from authorization (what you can do). Gates are simple closures registered in AuthServiceProvider that return true or false. They are appropriate for authorization logic not tied to a specific model. The Gate facade provides Gate::define(), Gate::allows(), Gate::denies(), and Gate::authorize(). Policies are classes that organize authorization logic around a specific Eloquent model. A policy class contains methods like view, create, update, delete, and restore that return booleans. Policies are registered in AuthServiceProvider's $policies array or auto-discovered when following Laravel's naming convention (PostPolicy for Post model). Policy methods receive the authenticated user and optionally the model instance. Policies are called via $this->authorize() in controllers, the Gate facade, or Blade's @can directive. The before() method in a policy runs before all other checks, allowing super-admin bypass logic. The viewAny() policy method controls whether a user can see the list/index page. Response objects can be returned from policy methods to provide custom error messages alongside the authorization result.
Example
<?php
// app/Policies/PostPolicy.php

namespace App\Policies;

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

class PostPolicy
{
    // Super-admin bypass (runs before all checks)
    public function before(User $user, string $ability): bool|null
    {
        return $user->isAdmin() ? true : null;
    }

    public function viewAny(User $user): bool
    {
        return true; // any authenticated user can list posts
    }

    public function view(?User $user, Post $post): bool
    {
        return $post->status === 'published' || $user?->id === $post->user_id;
    }

    public function create(User $user): bool
    {
        return $user->hasVerifiedEmail();
    }

    public function update(User $user, Post $post): Response
    {
        return $user->id === $post->user_id
            ? Response::allow()
            : Response::deny('You do not own this post.');
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

// In controller:
// $this->authorize('update', $post);

// In Blade:
// @can('update', $post) ... @endcan