---
title: "Gate & middleware — Permissions for Laravel | Roundly"
description: "Authorize with can: middleware, $user->can() and Gate — without ever overriding your model policies."
url: https://roundly-consulting.com/open-source/docs/permissions-for-laravel/gate-middleware
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Permissions for Laravel](https://roundly-consulting.com/open-source/docs/permissions-for-laravel.md)

# Gate & middleware

With register\_gate\_check on (the default), permissions resolve through Laravel’s Gate, so the native can: middleware works out of the box:

```php
Route::get('/users', UsersController::class)->middleware('can:auth.users.view');

$user->can('auth.users.view');           // true when granted directly or via a role
Gate::forUser($user)->allows('auth.users.view');
```

## How the hook decides

The package registers a single Gate::before hook. It answers only when all of the following hold — otherwise it returns null and your own gates and policies run untouched:

- The check carries no arguments — no model and no extra parameters.
- The user model has hasPermissionTo(), i.e. it uses HasRoles.
- The ability is the name of a registered permission.

When the user holds that permission — directly or via a role — the hook returns true. It never returns false: a user without the permission falls through to your gates, and with none defined Laravel denies, so the can: middleware responds with 403.

## Policies keep the final say

Permission checks never override model policies. As soon as a check carries a model or any argument — $user->can('update', $post), or the can:update,post middleware — the hook defers to your gate or policy. A permission named update authorizes $user->can('update'), but PostPolicy::update() still decides $user->can('update', $post):

```php
Permissions::permission('update');
Permissions::for($user)->givePermissionTo('update');

$user->can('update');          // true — argument-less, answered by the permission
$user->can('update', $post);   // PostPolicy::update() decides — ownership, tenancy, …
```

```php
final class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        return $post->owner_id === $user->getKey();
    }
}
```

Coarse permissions and model-scoped policies coexist without one silently swallowing the other.

## Disabling the hook

Set register\_gate\_check to false to skip the hook entirely and resolve permissions yourself:

```php
// config/permissions.php
'register_gate_check' => false,

// then check explicitly where you need it
abort_unless($request->user()->hasPermissionTo('auth.users.view'), 403);
```

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/permissions-for-laravel.md)
