vaened/laravel-authorization

Package to manage the permissions in a laravel application

Maintainers

Package info

github.com/vaened/laravel-authorization

Type:package

pkg:composer/vaened/laravel-authorization

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

v4.1.1 2026-07-11 18:33 UTC

README

Tests Software License

Roles, permissions, explicit denials, and route middleware for Laravel applications.

Built on top of PHP Sentinel.

// Authorizations
$cashier         = $this->roles->create('cashier', 'Cashier');
$createDocuments = $this->permissions->create('documents.create', 'Create Documents');
$annulDocuments  = $this->permissions->create('documents.annul', 'Annul Documents');

// Assignment
$cashier->grant($createDocuments, $annulDocuments);
$user->grant($cashier);

// Evaluation
$user->actsAs('cashier');             // true
$user->can('documents.create');       // true
$user->can('documents.annul');        // true

// Deny overrides direct or inherited grants
$user->deny($annulDocuments);
$user->can('documents.annul');        // false

Installation

Laravel Authorization requires PHP 8.4 or higher and can be installed via Composer:

composer require vaened/laravel-authorization

Publish the package resources:

php artisan vendor:publish --tag=laravel-authorization-config
php artisan vendor:publish --tag=laravel-authorization-migrations

Then run your migrations:

php artisan migrate

Configuration

By default, your user model uses the package's direct authorization API through the Authorizable interface and Authorizations trait, and Sentinel integrates with Laravel's Gate using the after strategy. See Advanced usage when you prefer Laravel's native API.

Using the direct model API

Laravel Authorization does not require you to extend a package-specific user model.

Instead, the user model you want to make authorizable only needs to:

use Illuminate\Foundation\Auth\User as Authenticatable;
use Vaened\Authorization\Authorizable;
use Vaened\Authorization\Authorizations;

class User extends Authenticatable implements Authorizable
{
    use Authorizations;
}

Once your user model uses the contract and trait above, it gains these capabilities:

Method Description
can(string ...$permissions): bool Checks whether the user has at least one of the given permissions.
cannot(string ...$permissions): bool Inverse of can.
actsAs(string ...$roles): bool Checks whether the user has at least one of the given roles.
actsNotAs(string ...$roles): bool Inverse of actsAs.
grant(Authorization ...$authorizations): void Grants roles or permissions to the user.
deny(Permission ...$permissions): void Explicitly denies permissions to the user.
revoke(Authorization ...$authorizations): void Removes a previous grant or denial from the user.

Authorization management

Use PHP Sentinel's RoleRegistry and PermissionRegistry to manage the role and permission catalogs. Both registries expose the same API; their only difference is the authorization type they manage.

use Vaened\Sentinel\Registry\PermissionRegistry;
use Vaened\Sentinel\Registry\RoleRegistry;

final readonly class AuthorizationCatalog
{
    public function __construct(
        private RoleRegistry $roles,
        private PermissionRegistry $permissions,
    ) {
    }
}
Method Description RoleRegistry result PermissionRegistry result
create(string $code, string $name, ?string $description = null) Creates a catalog entry. Role Permission
lookup(array $codes) Retrieves the entries whose codes were requested. Roles Permissions
find(string $code) Retrieves one entry by code, or null when it does not exist. Role|null Permission|null
update(int|string $id, string $name, ?string $description = null) Updates an existing entry. void void
remove(int|string $id) Removes an existing entry when it is no longer assigned. void void
$cashier = $this->roles->create('cashier', 'Cashier');
$read = $this->permissions->create('documents.read', 'Read Documents');

$cashier->grant($read);

$permissions = $this->permissions->lookup(['documents.read', 'documents.update']);
$permission = $this->permissions->find('documents.read');

Middleware

When Gate integration is enabled (the default), you can use Laravel's native can middleware for permission checks:

Route::middleware('can:posts.edit')->group(function () {
    // ...
});

Laravel's can middleware uses the Gate integration described in Laravel Gate. It is available as long as authorization.gate is not null.

Laravel Authorization also registers two package middleware aliases. They are useful when you want to invoke Sentinel directly, including when Gate integration is disabled, and when you need to check roles.

  • authorization.permissions allows the request only if the current authenticated user can perform at least one of the given permissions.
  • authorization.roles allows the request only if the current authenticated user acts as at least one of the given roles.
Route::middleware('authorization.permissions:posts.edit')->group(function () {
    // ...
});

Route::middleware('authorization.roles:admin')->group(function () {
    // ...
});

If authorization fails, the middleware throws Laravel’s AuthorizationException.

You can rename these aliases by publishing and editing the middlewares array in config/authorization.php.

Laravel Gate

Laravel Authorization can connect PHP Sentinel to Laravel's authorization Gate. This lets a compatible subject participate in Laravel's standard authorization features, including Gate::allows, the can route middleware, and Blade's @can directive.

Configure the gate option in config/authorization.php:

'gate' => 'after', // 'after', 'before', or null

The default is after. Choose another strategy only when your application needs different precedence:

Strategy Behavior Use it when
before Sentinel evaluates the ability before Laravel's own Gates and Policies. Its result always decides the check. Sentinel is the authoritative authorization system for the application.
after Laravel evaluates its own Gates and Policies first. Sentinel evaluates only when Laravel has no result. Recommended default; Sentinel acts as a fallback.
null No Sentinel callback is registered in Laravel's Gate. The application should use Sentinel directly or manage Gate itself.

Sentinel always resolves an ability to true or false: a subject either has the permission or it does not. It does not return Laravel's undecided null result. Consequently, before also denies abilities that Sentinel does not grant, while after preserves any explicit allow or denial already returned by Laravel.

Cache

Laravel Authorization caches each subject's authorization projection: its roles and the effective state of its permissions. The cache is updated or invalidated by the package when authorization assignments change.

You can configure it through the cache array in config/authorization.php.

  • store is the name of a store defined in your application's cache.stores configuration. Set it when authorization should use a dedicated Laravel cache store. When it is null, the package uses your application's default cache store.
  • prefix namespaces the package's authorization cache entries so they remain isolated from other cached application data.
  • ttl is the lifetime, in seconds, of a subject authorization projection. When it is null and the selected store supports cache tags, projections are kept permanently because the package can remove them explicitly. Stores without tag support use a twelve-hour TTL by default, so projections orphaned after a global invalidation eventually expire. Set an integer TTL to override it.

Database

The package ships with five tables that back the entire authorization model:

Table What it stores
permissions Atomic permissions (e.g. users.read, posts.publish). The catalog.
roles Named groupings of permissions. The catalog.
role_permissions Which permissions each role grants. Many-to-many between roles and permissions.
subject_roles Which roles each subject carries. Polymorphic — works with any authorizable model.
subject_permissions Direct grants and explicit denials on a subject. Polymorphic. A denial takes precedence over a direct or inherited grant.

You can rename any of these tables by publishing and editing the tables array in config/authorization.php. Each key corresponds to a table above.

Commands

authorization:cache:invalidate

Invalidates every authorization projection managed by the package:

php artisan authorization:cache:invalidate

Use it after authorization data is changed outside Laravel Authorization, such as through a direct database operation or an external integration.

Default models and repositories

This package provides the Laravel-side infrastructure for PHP Sentinel:

  • Eloquent repositories
  • package configuration
  • middleware integration
  • service provider wiring

It also includes default models for roles and permissions. When using the direct model API, your application user is the authorization subject: implement the Authorizable contract and use the Authorizations trait.

Advanced usage

The default setup is documented in Using the direct model API and Laravel Gate. This section only covers the alternative integration where the model uses Laravel's native authorization API.

Using Laravel's native authorization API

Use this mode when the application should use Laravel's own authorization API and does not need the package's direct model methods. Do not use the package's Authorizable interface or Authorizations trait. The model must implement Sentinel's Subject contract:

use Illuminate\Foundation\Auth\User as Authenticatable;
use Vaened\Sentinel\Identifier;
use Vaened\Sentinel\Subject;

class User extends Authenticatable implements Subject
{
    public function id(): int|string|Identifier
    {
        return $this->getKey();
    }
}

Illuminate\\Foundation\\Auth\\User already includes Laravel's native Authorizable trait. If your model extends Eloquent's base Model directly, use Illuminate\\Foundation\\Auth\\Access\\Authorizable on the model instead.

Use Laravel's own authorization implementation on the model. Keep gate => 'after' to let Sentinel serve as a fallback, use before only when Sentinel must take precedence, or use null when Laravel must operate without Sentinel Gate integration. See Laravel Gate for the exact precedence rules.

Without the package trait, manage assignments through the package facades:

use Vaened\Authorization\Facades\Denier;
use Vaened\Authorization\Facades\Granter;
use Vaened\Authorization\Facades\Revoker;

Granter::grant($user, $role);
Denier::deny($user, $permission);
Revoker::revoke($user, $permission);

Custom trait: If you want to expose these operations as methods on your model, create a custom trait based on Authorizations and keep only the methods you need, such as grant, deny, and revoke. Omit can because Laravel provides that authorization API in this mode.

Do not combine the package's Authorizations trait with Laravel's Authorizable trait. Both define can and cannot with different contracts.

Errors

Adapter-specific errors extend Sentinel’s base AuthorizationError.

For example, if a subject used by the Laravel adapter does not extend Eloquent Model, the package throws:

  • UnsupportedSubject

Middleware authorization failures continue to use Laravel’s own AuthorizationException.

Development

make composer-install
make test

Additional documentation

You can find more details in the source code as well as in the tests located in tests/.

The tests cover different usage scenarios and can serve as additional reference for understanding the library’s behavior.