> ## 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 (gates and policies)

> Learn how to manage user action permissions with Laravel's gates and policies.

## Authentication vs. authorization

**Authentication** verifies "who" a user is. Signing in is the canonical example.
**Authorization** decides "what" that user is allowed to do. For example, you might let users edit only their own posts, or restrict configuration changes to administrators.

Once you've implemented sign-in through [authentication](./authentication), authorization is your next step.

<Info>
  Laravel's authorization features offer two approaches: gates and policies. Which to use depends on your use case.
</Info>

**How to choose**

| Scenario | Recommended |
| - | - |
| Simple checks not tied to a specific model | Gate |
| Managing CRUD permissions on a model | Policy |
| Restricting access to an admin dashboard | Gate |
| Permissions for creating, editing, and deleting blog posts | Policy |

## Gates

Gates are simple closure-based authorization checks. They're well suited to permission checks that aren't tied to a particular model.

### Authorization flow with gates

```mermaid theme={null}
flowchart TD
    A["AppServiceProvider::boot()<br>(App\\Providers\\AppServiceProvider)"] --> B["Gate::define('ability', fn)<br>Register the authorization logic"]
    C["Authorization check from a controller"] --> D{"Is Gate::before()<br>configured?"}
    D -->|"Yes"| E{"Return value of before()"}
    E -->|"true / false"| F["Use the before() result as the final decision"]
    E -->|"null"| G["Run the closure registered by define()"]
    D -->|"No"| G
    G --> H{"Return value of the closure"}
    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>→ automatic 403 Forbidden"]
```

### Defining a gate

Define gates in the `boot` method of `App\Providers\AppServiceProvider` using `Gate::define()`.

```php theme={null}
// app/Providers/AppServiceProvider.php

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

A gate closure always receives the currently authenticated user as its first argument. Additional information — such as the target model — can be passed as further arguments.

### Checking permissions with a gate

To check permissions in a controller, use `Gate::allows()` or `Gate::denies()`.

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

public function update(Request $request, Post $post): RedirectResponse
{
    if (! Gate::allows('update-post', $post)) {
        abort(403);
    }

    // Update the post...

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

### Throwing exceptions with Gate::authorize()

Use `Gate::authorize()` to automatically return a 403 response when the user is unauthorized. You can skip the manual `abort(403)`.

```php theme={null}
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);

    // Reached only when the user is authorized
    // Update the post...

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

<Tip>
  When unauthorized, `Gate::authorize()` throws `Illuminate\Auth\Access\AuthorizationException`. Laravel automatically converts it into a 403 HTTP response.
</Tip>

### Administrator bypass (the before method)

To grant administrators every permission, use `Gate::before()`.

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

public function boot(): void
{
    // $ability contains the name of the gate being checked (for example, 'update-post')
    Gate::before(function (User $user, string $ability) {
        if ($user->isAdministrator()) {
            return true;
        }
    });

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

If the `before` closure returns anything other than `null`, that result becomes the final permission decision. Returning `null` (or nothing) falls through to the normal gate check.

### @can / @cannot in Blade templates

The `@can` and `@cannot` directives make it easy to run gate checks in your templates.

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

@cannot('update-post', $post)
    <p>You don't have permission to edit this post.</p>
@endcannot
```

## Policies

A policy is a class that groups authorization logic related to a particular model. For managing create, view, update, and delete permissions on a `Post` model, a policy is a better fit than a gate.

### Authorization flow with a policy

```mermaid theme={null}
flowchart TD
    A["HTTP request"] --> B["Controller"]
    B --> C["Gate::authorize('update', $post)"]
    C --> D{"Is PostPolicy::before()<br>defined?"}
    D -->|"Yes"| E{"Return value of before()"}
    E -->|"true / false"| F["Use the before() result as the final decision"]
    E -->|"null"| G["Run 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 a policy

Use the `make:policy` Artisan command to generate a policy class.

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

Use the `--model` option to scaffold a policy that includes every CRUD method for that model.

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

`app/Policies/PostPolicy.php` is generated.

### Model and policy auto-discovery

By default, Laravel automatically discovers policies based on a naming convention.

* Model: `app/Models/Post.php`
* Policy: `app/Policies/PostPolicy.php`

As long as you follow the convention, you don't need to register the policy yourself.

<Info>
  If you deviate from the naming convention or want to register a policy manually, use `Gate::policy()` in the `boot` method of `AppServiceProvider`.

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

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

  In Laravel 13, you can also register a policy declaratively by attaching the `#[UsePolicy]` attribute to the model.

  ```php theme={null}
  use App\Policies\PostPolicy;
  use Illuminate\Database\Eloquent\Attributes\UsePolicy;

  #[UsePolicy(PostPolicy::class)]
  class Post extends Model {}
  ```
</Info>

### Implementing policy methods

A policy generated with the `--model` option includes methods that correspond to the standard CRUD actions.

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

namespace App\Policies;

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

class PostPolicy
{
    /**
     * Whether the user may view the post list.
     */
    public function viewAny(User $user): bool
    {
        return true; // Any authenticated user may view
    }

    /**
     * Whether the user may view a specific post.
     */
    public function view(User $user, Post $post): bool
    {
        return true; // Any authenticated user may view
    }

    /**
     * Whether the user may create a post.
     */
    public function create(User $user): bool
    {
        return true; // Any authenticated user may create posts
    }

    /**
     * Whether the user may update a post.
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id; // Only the author may edit
    }

    /**
     * Whether the user may delete a post.
     */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id; // Only the author may delete
    }
}
```

### Administrator bypass (the before method)

Policies also support a `before` method that grants administrators every permission.

```php theme={null}
/**
 * A pre-check executed before any other policy method.
 * $ability contains the policy method name (for example, 'update' or 'delete').
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true; // Administrators are allowed every action
    }

    return null; // Returning null falls through to the normal policy method
}
```

<Warning>
  The `before` method is invoked only when a matching method exists on the policy class. For example, if there is no `update` method, `before` is not called for `update`.
</Warning>

### Order of before callbacks

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

## Using policies from controllers

### The authorize() method

Laravel controllers include an `authorize()` helper method (when you're not extending the base controller, use `Gate::authorize()` instead).

<Tabs>
  <Tab title="Gate::authorize()">
    ```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
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            Gate::authorize('update', $post);

            // Update the post...

            return redirect()->route('posts.index');
        }

        public function store(Request $request): RedirectResponse
        {
            Gate::authorize('create', Post::class);

            // Create the post...

            return redirect()->route('posts.index');
        }

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

            $post->delete();

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

  <Tab title="User::can() / cannot()">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

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

    class PostController extends Controller
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            if ($request->user()->cannot('update', $post)) {
                abort(403);
            }

            // Update the post...

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

### Registering RESTful policies in bulk with authorizeResource()

Calling `authorizeResource()` in the constructor automatically wires each controller action to the corresponding policy method.

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

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }

    // The corresponding policy method is applied automatically to
    // index(), show(), create(), store(), edit(), update(), and destroy()
}
```

Mapping between controller actions and policy methods:

| Controller action | Policy method |
| - | - |
| `index` | `viewAny` |
| `show` | `view` |
| `create` / `store` | `create` |
| `edit` / `update` | `update` |
| `destroy` | `delete` |

<Tip>
  With `authorizeResource()`, you no longer need to write an individual `authorize()` call in each action. It's especially convenient when paired with RESTful resource controllers.
</Tip>

## Authorization via middleware

To perform authorization at the route level, use the `can` middleware.

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

// Accessible only to users authorized to update the post
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// Accessible only to users authorized to create posts
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:create,App\Models\Post');
```

A shorter form with the `can` method:

```php theme={null}
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->can('update', 'post');
```

## Using policies in Blade templates

Once a policy is registered, Blade's `@can` / `@cannot` directives automatically use it.

```blade theme={null}
{{-- Show the edit button only to authorized users --}}
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}" class="btn">Edit</a>
@endcan

{{-- Same idea for the delete button --}}
@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">Delete</button>
    </form>
@endcan

{{-- New post button --}}
@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}">New post</a>
@endcan
```

## Practical example: managing blog post permissions

Here's how to implement "only the author can edit or delete their own posts" in a blog application.

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

  <Step title="Implement the policy methods">
    ```php theme={null}
    <?php

    namespace App\Policies;

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

    class PostPolicy
    {
        /**
         * Grant administrators every permission.
         */
        public function before(User $user, string $ability): bool|null
        {
            if ($user->is_admin) {
                return true;
            }

            return null;
        }

        public function viewAny(User $user): bool
        {
            return true;
        }

        public function view(User $user, Post $post): bool
        {
            return true;
        }

        public function create(User $user): bool
        {
            return true;
        }

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

  <Step title="Add authorizeResource() to the controller">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

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

    class PostController extends Controller
    {
        public function __construct()
        {
            $this->authorizeResource(Post::class, 'post');
        }

        public function index(): View
        {
            $posts = Post::latest()->paginate(10);
            return view('posts.index', compact('posts'));
        }

        public function store(Request $request): RedirectResponse
        {
            $post = $request->user()->posts()->create(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

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

        public function update(Request $request, Post $post): RedirectResponse
        {
            $post->update(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

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

        public function destroy(Post $post): RedirectResponse
        {
            $post->delete();

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

  <Step title="Add permission checks to Blade templates">
    ```blade theme={null}
    {{-- resources/views/posts/show.blade.php --}}
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->body }}</p>
    <p>Author: {{ $post->user->name }}</p>

    @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>

## Summary

<AccordionGroup>
  <Accordion title="Choosing between gates and policies">
    * **Gates**: simple permission checks that aren't tied to a model — such as access to an admin dashboard or permission to change global settings.
    * **Policies**: managing CRUD permissions for a model. Create a policy class for each resource such as `Post`, `Order`, or `Comment`.
  </Accordion>

  <Accordion title="Common APIs">
    ```php theme={null}
    // Check with a gate
    Gate::allows('update-post', $post);      // true/false
    Gate::denies('update-post', $post);      // true/false
    Gate::authorize('update-post', $post);   // throws on failure

    // Check on the user model
    $user->can('update', $post);     // true/false
    $user->cannot('update', $post);  // true/false

    // Check in a controller
    Gate::authorize('update', $post);        // 403 on failure

    // Check in Blade
    @can('update', $post) ... @endcan
    @cannot('update', $post) ... @endcannot
    ```
  </Accordion>

  <Accordion title="Common Artisan commands">
    ```shell theme={null}
    # Generate an empty policy
    php artisan make:policy PostPolicy

    # Generate a policy with CRUD methods for the model
    php artisan make:policy PostPolicy --model=Post
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Controller PHP Attributes](/en/advanced/controller-attributes.md)
- [Laravel Telescope](/en/telescope.md)
- [Error Handling](/en/error-handling.md)
- [Encryption](/en/encryption.md)
- [Laravel Pulse](/en/pulse.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.