---
title: "Authorization — Approvals for Laravel | Roundly"
description: "Gate every decision path through a Laravel Gate ability your app defines — denied actors get UnauthorizedApprovalException."
url: https://roundly-consulting.com/open-source/docs/approvals-for-laravel/authorization
language: en
---

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

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

# Authorization

Set approvals.authorization.enabled to true — or the env variable, which also accepts 1, yes and on (false, 0, no and off switch it off, a blank value leaves it off; anything else throws InvalidConfigurationException) — to gate every decision path through a Gate ability:

```bash
APPROVALS_AUTHORIZATION=true
```

The package never defines the gate — your app does:

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

Gate::define('decide-approval', fn ($user, $approvable) => $user->can('review', $approvable));
```

A denied gate throws UnauthorizedApprovalException before anything is recorded:

```php
use RoundlyConsulting\Approvals\Exceptions\UnauthorizedApprovalException;
use RoundlyConsulting\Approvals\Facades\Approvals;

try {
    Approvals::for($deployment)->as($intern)->approve();
} catch (UnauthorizedApprovalException $e) {
    // the gate denied this actor — nothing was recorded
}
```

## How the check runs

- It guards approve(), reject(), toggle(), ask() and cancel() — through the facade, the traits, the test helpers and the actions alike. Closing a round with close() runs no gate — authorize it in your own code.
- The ability is checked with Gate::forUser() against the approvable, for the model that acts, so the actor doesn’t have to be the logged-in user. For ask() it is the asked actor.
- With delegation, the gate checks the acting delegate, not its delegator.
- The named-approver check on requests is separate and always on.
- The check is internal to the decision actions — there is no public hook to ask the gate without making a decision. Code that needs the same answer elsewhere has to copy the logic, as requests-for-laravel’s DecisionGate does for reopen(); if the way approvals reads its gate changes, that copy has to follow.

## A custom ability name

```php
// config/approvals.php
'authorization' => [
    'enabled' => env('APPROVALS_AUTHORIZATION', false),
    'ability' => 'review-deployment',
],

// App\Providers\AppServiceProvider::boot()
Gate::define('review-deployment', fn (User $user, Deployment $deployment) => $user->team_id === $deployment->team_id);
```

Not set (null or blank) means decide-approval; a non-string ability throws InvalidConfigurationException rather than checking a different gate.

## 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/approvals-for-laravel.md)
