binary-cats/rbac
Binary Cats RBAC is a Laravel role-based access control package for managing roles, permissions, and guards. It provides middleware, Blade directives, caching, and a fluent API to secure routes, controllers, and UI elements with minimal configuration.
composer require binary-cats/laravel-rbac
boot() that publishes a config file and migrations. Run php artisan vendor:publish --tag=rbac (check the provider for the exact tag) and then php artisan migrate.User model — typically something like HasRoles, exposing assignRole(), hasPermissionTo(), hasRole(), etc. Confirm the exact trait name in the package src/ root.RolesAndPermissionsSeeder that creates roles (admin, editor, viewer) and permissions (posts.create, posts.edit, posts.delete). Prefer firstOrCreate so the seeder is idempotent and safe in CI.Route::post('/posts', [PostController::class, 'store'])
->middleware('permission:posts.create');
then in a policy or controller use $user->can('posts.create').Route middleware for coarse access. Use the permission middleware on route groups (admin area, API resources) rather than sprinkling checks in every controller. Keep role-based middleware for "is this a staff user at all" and permission checks for the specific action.
Policies for fine-grained, model-scoped rules. RBAC should answer what a user can do in general; policies answer whether they can do it to this record (ownership, tenant, status). Wire them together:
public function update(User $user, Post $post): bool
{
return $user->hasPermissionTo('posts.edit')
&& ($post->author_id === $user->id || $user->hasRole('admin'));
}
Blade directives for UI affordances. Hide buttons with @can('posts.delete', $post) / an RBAC directive rather than re-implementing checks in views. Always keep the server-side check too — the Blade check is cosmetic.
Seeder-driven catalogues. Treat the permission list as data, not ad-hoc strings. A config/permissions.php or enum of permission names makes refactors safe and lets you diff the catalogue in code review. Group permissions by resource with a consistent resource.action convention.
Cache after write, not on read. After assignRole/syncPermissions/givePermissionTo in tests or admin UI, refresh the cache (or flush the user's cache entry). For bulk operations (importing users, re-seeding), wrap in a transaction and flush once at the end.
Form requests for validation of role assignment. When building an admin UI to manage roles, validate that role names exist before calling syncRoles — the package will usually throw on unknown names, which surfaces as a 500 rather than a 422.
Testing helpers. In feature tests, seed only what the test needs ($this->seed(RolesAndPermissionsSeeder::class) in setUp is fine for small suites) and assert both the allow and deny path. A permission test that only checks the happy path hides regressions.
Laravel Boost integration (new in 2.4.0). The release adds Boost guidelines. If you use Laravel Boost / AI-assisted coding, the package now ships guidelines that help generated code follow its conventions (correct trait names, middleware aliases, cache invalidation). Run your Boost setup after upgrading so the guidelines are picked up; it costs nothing and reduces the chance of an assistant inventing a hasPermission() API that doesn't exist.
Cache::forget helper or an artisan command) before debugging the logic. This bites hardest in long-running workers and in test suites that reuse the same user.users table. If you publish migrations with timestamps that sort before your user migration, foreign keys fail on a fresh database. Rename the published migrations if needed.posts.edit to post.update later means a data migration over the permission table plus every middleware, policy, and Blade check. Settle on a convention (plural resource + dot + action) on day one.before() hook or a config flag for a bypass role. Prefer the gate hook over if ($user->isAdmin()) return true; scattered through policies — one place, auditable.Role/Permission model classes and table names. If you need audit columns (assigned_by, assigned_at), subclass the models and rebind in config rather than editing vendor code.bootstrap/app.php / Kernel.php depending on Laravel version.How can I help you explore Laravel packages today?