> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Authorization

> Control what authenticated users can do using Laravel's gates and policies—two complementary tools for organizing authorization logic.

## Gates vs. policies

Laravel provides two primary tools for authorization: **gates** and **policies**.

* **Gates** are closures. Use them for simple checks that aren't tied to a specific model, such as "can this user access the admin dashboard?"
* **Policies** are classes. Use them to group authorization logic around a particular model or resource.

Most applications use both. Gates handle broad, model-independent checks; policies handle per-resource rules.

## Gates

### Gate authorization flow

```mermaid theme={null}
flowchart TD
    A["AppServiceProvider::boot()<br>(App\\Providers\\AppServiceProvider)"] --> B["Gate::define('ability', fn)<br>Register authorization logic"]
    C["Authorization check from controller"] --> D{"Gate::before()<br>callback set?"}
    D -->|"Yes"| E{"before() return value"}
    E -->|"true / false"| F["Use before() result as final decision"]
    E -->|"null"| G["Execute define() closure"]
    D -->|"No"| G
    G --> H{"Closure return value"}
    H -->|"true"| I["✓ Access allowed"]
    H -->|"false"| J["✗ Access denied"]
    F -->|"true"| I
    F -->|"false"| J
    J -->|"Gate::allows()"| K["Returns false<br>→ call abort(403) manually"]
    J -->|"Gate::authorize()"| L["AuthorizationException<br>→ 403 Forbidden automatically"]
```

### Defining gates

Define gates inside the `boot` method of `App\Providers\AppServiceProvider` using the `Gate` facade. Gates receive the authenticated user as their first argument, plus any additional arguments you pass:

```php theme={null}
use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

### Checking gates

Call `Gate::allows` or `Gate::denies` in a controller to guard an action:

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

class PostController extends Controller
{
    /**
     * Update the given post.
     */
    public function update(Request $request, Post $post): RedirectResponse
    {
        if (! Gate::allows('update-post', $post)) {
            abort(403);
        }

        // Update the post...

        return redirect('/posts');
    }
}
```

To throw an exception automatically instead of calling `abort`, use `Gate::authorize`:

```php theme={null}
Gate::authorize('update-post', $post);

// Continues if authorized, throws AuthorizationException if not...
```

### Gate responses

Return a detailed `Response` from a gate instead of a boolean to include an error message:

```php theme={null}
use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
        ? Response::allow()
        : Response::deny('You must be an administrator.');
});
```

Inspect the full response with `Gate::inspect`:

```php theme={null}
$response = Gate::inspect('edit-settings');

if ($response->allowed()) {
    // Authorized...
} else {
    echo $response->message();
}
```

## Policies

### Policy authorization flow

```mermaid theme={null}
flowchart TD
    A["HTTP Request"] --> B["Controller"]
    B --> C["Gate::authorize('update', $post)"]
    C --> D{"PostPolicy::before()<br>defined?"}
    D -->|"Yes"| E{"before() return value"}
    E -->|"true / false"| F["Use before() result as final decision"]
    E -->|"null"| G["Execute PostPolicy::update(User, Post)"]
    D -->|"No"| G
    G --> H{"$user->id === $post->user_id?"}
    H -->|"true"| I["✓ Continue processing"]
    H -->|"false"| J["✗ AuthorizationException<br>→ 403 Forbidden"]
    F -->|"true"| I
    F -->|"false"| J
```

### Generating policies

Create a policy class with Artisan:

```shell theme={null}
php artisan make:policy PostPolicy --model=Post
```

Policy classes live in `app/Policies`. The `--model` flag pre-fills CRUD method stubs.

### Auto-discovery

Laravel automatically discovers policies by convention. A model at `App\Models\Post` is paired with a policy at `App\Policies\PostPolicy`. To register a policy explicitly, call `Gate::policy` inside `AppServiceProvider::boot`:

```php theme={null}
use App\Models\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;

Gate::policy(Post::class, PostPolicy::class);
```

### Writing policy methods

Each method receives the authenticated user and, usually, the model instance. Return a boolean (or a `Response`) to allow or deny:

```php theme={null}
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * Determine whether the user can update the post.
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    /**
     * Determine whether the user can delete the post.
     */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}
```

### Policy responses

Return a `Response` to attach a custom message to a denial:

```php theme={null}
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

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

### Actions without a model instance

Some policy methods, like `create`, don't operate on an existing model. Define them with only the user argument:

```php theme={null}
/**
 * Determine whether the user can create posts.
 */
public function create(User $user): bool
{
    return $user->role === 'author';
}
```

### Guest users

By default, gates and policies return `false` for unauthenticated users. To allow guests through to the policy, mark the user argument as nullable:

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

### Policy filters

To give a user unrestricted access before any other checks run, define a `before` method on the policy:

```php theme={null}
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true;
    }

    return null; // Fall through to the specific policy method
}
```

Returning `null` from `before` delegates to the specific policy method.

### Before callback execution order

```mermaid theme={null}
flowchart TD
    A["Authorization check starts<br>authorize('update', $post)"] --> B["Run PostPolicy::before()"]
    B --> C{"Return value is null?"}
    C -->|"Non-null (true / false)"| D["Use before() result as final decision"]
    C -->|"null"| E["Run the specific policy method<br>update() / delete() etc."]
    E --> F{"Policy method return value"}
    F -->|"true"| G["✓ Access allowed"]
    F -->|"false"| H["✗ Access denied → 403"]
    D -->|"true (admin bypass)"| G
    D -->|"false"| H
```

## Authorizing actions using policies

### Via the user model

The `User` model exposes `can` and `cannot` methods. Pass the ability and the model instance:

```php theme={null}
if ($request->user()->can('update', $post)) {
    // Authorized...
}

if ($request->user()->cannot('delete', $post)) {
    abort(403);
}
```

For actions without a model instance (like `create`), pass the policy class instead:

```php theme={null}
if ($request->user()->can('create', Post::class)) {
    // ...
}
```

### Via the Gate facade

```php theme={null}
use Illuminate\Support\Facades\Gate;

if (Gate::allows('update', $post)) {
    // ...
}

Gate::authorize('update', $post); // Throws on denial
```

### Via middleware

Use the `can` middleware to guard routes:

```php theme={null}
use App\Models\Post;

Route::put('/posts/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');
```

For actions without a model instance:

```php theme={null}
Route::post('/posts', function () {
    // The current user may create posts...
})->middleware('can:create,App\Models\Post');
```

### Via Blade templates

Use `@can`, `@cannot`, and `@canany` directives in Blade views:

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

@cannot('delete', $post)
    <p>You cannot delete this post.</p>
@endcannot

@canany(['update', 'delete'], $post)
    <div class="post-actions">...</div>
@endcanany
```

## A complete example

<Steps>
  <Step title="Generate the policy">
    ```shell theme={null}
    php artisan make:policy PostPolicy --model=Post
    ```
  </Step>

  <Step title="Define authorization rules">
    ```php theme={null}
    <?php

    namespace App\Policies;

    use App\Models\Post;
    use App\Models\User;

    class PostPolicy
    {
        public function create(User $user): bool
        {
            return $user->role === 'author';
        }

        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->isAdmin();
        }
    }
    ```
  </Step>

  <Step title="Protect the controller">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\Request;
    use Illuminate\Http\RedirectResponse;

    class PostController extends Controller
    {
        public function store(Request $request): RedirectResponse
        {
            $this->authorize('create', Post::class);

            $post = Post::create($request->validated());

            return redirect()->route('posts.show', $post);
        }

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

            $post->update($request->validated());

            return redirect()->route('posts.show', $post);
        }

        public function destroy(Post $post): RedirectResponse
        {
            $this->authorize('delete', $post);

            $post->delete();

            return redirect()->route('posts.index');
        }
    }
    ```
  </Step>

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

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

<Tip>
  The `$this->authorize` method in a controller automatically throws an `AuthorizationException` (HTTP 403) if the check fails. You don't need to call `abort(403)` manually.
</Tip>
