Laravel Packages

Grant: Enum-Based Roles and Permissions for Laravel

Published
Grant: Enum-Based Roles and Permissions for Laravel image

Grant is a roles and permissions package for Laravel by Pushpak Chhajed. You define roles and permissions in PHP enums, while the database stores each user's role assignments. Checks go through Laravel's Gate, so existing calls to can(), Gate::authorize(), and Blade's @can work with Grant.

Define access in enums

A permission enum implements Grant's Ability contract, with each backing value registered as an ability in Laravel's Gate. A role enum implements the Role contract and lists the permissions it grants:

use Shipfastlabs\Grant\Ability;
use Shipfastlabs\Grant\Role as RoleContract;
 
enum Permission: string implements Ability
{
case EditPosts = 'edit-posts';
case ViewReports = 'view-reports';
 
public function deniedMessage(): string
{
return 'You are not allowed to do that.';
}
}
 
enum Role: string implements RoleContract
{
case Editor = 'editor';
case Viewer = 'viewer';
 
public function permissions(): array
{
return match ($this) {
self::Editor => [Permission::EditPosts],
self::Viewer => [Permission::ViewReports],
};
}
}

Grant uses roles for assignment and permissions for checks. It has no role middleware or @role directive; routes use Laravel's authorization features to check a permission. If you want more background on defining abilities in code, see our article on authorization with backed enums.

Assign global or scoped roles

After adding Grant's HasRoles trait to the user model, you can manage assignments with grant(), revoke(), and syncRoles(). Passing an Eloquent model as on scopes a role to that team, project, or other model:

$user->grant(Role::Editor);
$user->grant(Role::Viewer, on: $team);
 
$user->can(Permission::EditPosts);
$user->can(Permission::ViewReports, $team);

A scoped permission check includes the user's global roles as well as roles assigned on that model. For another approach to scoped permissions, see Larallow.

Combine permissions with policies

Policies can still handle model rules such as ownership. Grant's Requires attribute checks the permission before the policy method runs, leaving the method to check the post itself:

use Shipfastlabs\Grant\Requires;
 
#[Requires(Permission::EditPosts)]
public function update(User $user, Post $post): bool
{
return $post->author_id === $user->id;
}

Grant forwards Gate arguments to this check, including a model used for scoped roles. You can also configure one role as a super admin; only a globally assigned super admin role bypasses Gate checks.

Install and maintain roles

Grant requires PHP 8.3 or later and Laravel 12 or 13. The install command publishes its configuration and migration, and creates starter role and permission enums if they do not exist:

composer require shipfastlabs/grant
php artisan grant:install
php artisan migrate

After installation, add Shipfastlabs\Grant\HasRoles to the user model. Changing a role's backing value or deleting a case leaves its old assignments orphaned, which means those users lose the role until you remap the rows. Run php artisan grant:sync to remap or delete them; grant:list and grant:show help inspect registered permissions and a user's roles.

See the Grant repository for configuration, testing helpers, and examples of scoped roles.

Yannick Lyn Fatt photo

Staff Writer at Laravel News and Full stack web developer.

Sponsored

masteringlaravel logo
Laravel Code Review

Get expert guidance in a few days with a Laravel code review

Visit Laravel Code Review

The latest

View all →
Synapse: A Dev Dashboard for Laravel AI SDK Agents image

Synapse: A Dev Dashboard for Laravel AI SDK Agents

Read article
Decide with Jev: Three Questions in One Request with the Laravel AI SDK image

Decide with Jev: Three Questions in One Request with the Laravel AI SDK

Read article
Securing Filament plugins with Plumb image

Securing Filament plugins with Plumb

Read article
LayaPHP: Self-Hosted Text Classification for PHP and Laravel image

LayaPHP: Self-Hosted Text Classification for PHP and Laravel

Read article
WhenMounted and BigInt Props in Inertia.js v3.8 image

WhenMounted and BigInt Props in Inertia.js v3.8

Read article
Postcodes for Laravel: GB Postcode Lookup and Geography Data image

Postcodes for Laravel: GB Postcode Lookup and Geography Data

Read article