# Authorization with Laravel Gates and Policies

> How to centralize authorization logic in Laravel using Gates and Policies, keeping 'who can do what' decisions clean and testable.

- Published: 2017-10-01
- Category: Web Development
- Tags: Laravel
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/authorization-with-laravel-gates-and-policies/
- Language: en-US
- Author: Muhammet Şafak

---
Authorization — the question "can this user perform this action?" — has to be answered somewhere in every application. The question is simple, but where you answer it matters. Writing checks like `if ($user->id !== $post->user_id)` inside controllers looks harmless at first; but once the same check is scattered across several places, changing a single rule means hunting down dozens of spots.

Laravel's **Gate** and **Policy** system makes these decisions centralized and testable.

## What is a Gate?

A **Gate** is used for simple, non-model-based authorization checks. It is defined inside `AuthServiceProvider`:

```php
use Illuminate\Support\Facades\Gate;

Gate::define('update-settings', function ($user) {
    return $user->is_admin;
});
```

Using it:

```php
if (Gate::allows('update-settings')) {
    // Admin action
}

// Or shorthand:
Gate::authorize('update-settings'); // Throws 403 if not allowed
```

It also works in Blade templates:

```blade
@can('update-settings')
    <a href="/settings">Settings</a>
@endcan
```

## What is a Policy?

A **Policy** is a class that groups the authorization logic specific to an Eloquent model. For models with multiple actions, it provides a cleaner structure than a Gate.

To generate one:

```bash
php artisan make:policy PostPolicy --model=Post
```

The generated `PostPolicy` class defines authorization rules as methods:

```php
namespace App\Policies;

use App\User;
use App\Post;

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

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

It needs to be registered in `AuthServiceProvider`:

```php
protected $policies = [
    Post::class => PostPolicy::class,
];
```

## Using a Policy

Inside a controller:

```php
public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);

    // Execution only reaches here if authorized
    $post->update($request->validated());
    return redirect()->route('posts.show', $post);
}
```

The `authorize('update', $post)` call invokes `PostPolicy@update` with the current user and the `$post` instance. If it returns `false`, a 403 response is automatically generated.

On the Blade side:

```blade
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">Edit</a>
@endcan

@can('delete', $post)
    <form action="{{ route('posts.destroy', $post) }}" method="POST">
        @csrf @method('DELETE')
        <button>Delete</button>
    </form>
@endcan
```

## Gate or Policy?

When choosing between the two, I apply a simple rule:

- If it is not tied to a specific model and involves a single check, a **Gate** is sufficient.
- If a model requires checks for multiple actions (view, update, delete, publish...), a **Policy** is the better fit.

For areas like user management or content management, setting up a Policy consolidates all the authorization logic for that domain into a single file. When a rule changes, there is only one place to open.

## Testing a Policy

Another area where this structure pays off is testability. Because a Policy is a plain class, it can be instantiated and tested directly — no need to bootstrap the controller:

```php
public function test_kullanici_kendi_yazilarini_guncelleyebilir()
{
    $user = User::factory()->create();
    $post = Post::factory()->for($user)->create();
    $policy = new PostPolicy();

    $this->assertTrue($policy->update($user, $post));
}

public function test_kullanici_baskasinin_yazisini_guncelleyemez()
{
    $yazar  = User::factory()->create();
    $misafir = User::factory()->create();
    $post    = Post::factory()->for($yazar)->create();
    $policy  = new PostPolicy();

    $this->assertFalse($policy->update($misafir, $post));
}
```

Writing these tests against an `if` block buried in a controller is not feasible — you would be forced to test that block indirectly through an HTTP request. With a Policy, the rule is tested directly, quickly, and in isolation.

## The `before` method: the admin bypass

Policy classes support a special method named `before`. This method runs before all other policy methods, and if it returns a non-`null` value, the other methods are never evaluated:

```php
class PostPolicy
{
    public function before(User $user, string $ability): ?bool
    {
        if ($user->is_superadmin) {
            return true; // Super admin can do everything
        }

        return null; // Continue to normal flow
    }

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

Returning `null` here is critical: if you returned `false`, you would block everyone — including the super admin. Returning `null` means "I'm not making a decision; check the next method." This is a common mistake for anyone seeing this pattern for the first time.

## Before policies

Before adopting this approach, I had spread authorization checks across controllers in some places and middleware in others. The rule for who owned a model was living in three different files, each written slightly differently. With a Policy, I pulled all of that into a single class; when a rule changes, there is one file to look at. When a new role is introduced or an existing rule is relaxed, it means making the change in one place and running the tests. That confidence simply was not possible with scattered `if` blocks.

Confusing authentication with authorization was another early mistake. Authentication answers "who is this person?"; authorization answers "can this person do this?" In [Laravel](/en/blog/laravel-5-2-multiple-auth-guards-and-route-groups/), these two layers are separate: `Auth` and middleware handle authentication, while `Gate` and `Policy` handle authorization. Once I clarified that distinction, it became obvious where to write what. A user may be logged in, but that does not mean they can access every resource — authenticated but unauthorized are two different states, two different layers.
