Covers Spatie Laravel Permission for role-based access control: roles and
permissions, route middleware, Blade authorization directives, multi-tenant
team scoping, wildcard/hierarchical permissions, super-admin bypass, cache
management, direct-vs-role permission inheritance, query scopes,
event-based audit logging, Policy integration, Sanctum/Passport API usage,
custom models (UUID support), and testing patterns.
Laravel Permission (Spatie)
Agent Workflow (MANDATORY)
Before ANY implementation, spawn 3 agents in parallel, one Agent call each with a name:
- fuse-ai-pilot:explore-codebase - Check existing auth patterns
- fuse-ai-pilot:research-expert - Verify Spatie Permission docs via Context7
- mcp__context7__query-docs - Check Laravel authorization patterns
After implementation, run fuse-ai-pilot:sniper for validation.
Overview
Spatie Laravel Permission provides complete role-based access control (RBAC) for Laravel applications.
| Component | Purpose |
|---|
| Role | Group of permissions (admin, writer) |
| Permission | Single ability (edit articles) |
| Middleware | Route protection |
| Blade Directives | UI authorization |
| Teams | Multi-tenant scoping |
| Wildcards | Hierarchical permissions |
| Super Admin | Bypass all checks |
| Events | Audit logging (v6.15.0+) |
| Query Scopes | Filter users by role/permission |
| API Support | Sanctum/Passport integration |
| Policies | Resource-based authorization |
Critical Rules
- Seed roles/permissions in
DatabaseSeeder
- Cache reset after changes:
php artisan permission:cache-reset
- Use kebab-case for naming:
edit-articles
- Never hardcode role checks in controllers - use middleware
- Set team context early in request for multi-tenant apps
- Specify guard for API -
permission:edit,api
- Clear cache in tests - Reset in setUp()/beforeEach()
Reference Guide
Core Concepts
Advanced Features
Integration
Operations & Quality
Templates (Code Examples)
Setup & Seeding
Routes & Middleware
Teams & Multi-Tenant
Super Admin & Cache
API Integration
Policies & Events
Testing
Custom Models
Quick Reference
Assign Role
$user->assignRole('admin');
Check Permission
$user->can('edit articles');
Middleware (Web)
Route::middleware(['role:admin'])->group(fn () => ...);
Middleware (API)
Route::middleware(['auth:sanctum', 'permission:edit,api'])->group(fn () => ...);
Blade
@role('admin') ... @endrole
@can('edit articles') ... @endcan
Query Scopes
User::role('admin')->get();
User::permission('edit articles')->get();
Teams
setPermissionsTeamId($team->id);
Wildcards
$role->givePermissionTo('articles.*');
Super Admin
Gate::before(fn ($user, $ability) =>
$user->hasRole('Super-Admin') ? true : null
);
Testing
beforeEach(fn () => app(PermissionRegistrar::class)->forgetCachedPermissions());
Feature Matrix
| Feature | Status | Reference |
|---|
| Basic RBAC | ✅ | spatie-permission.md |
| Middleware | ✅ | middleware.md |
| Blade Directives | ✅ | blade-directives.md |
| Multi-Guard (web/api) | ✅ | middleware.md, api-usage.md |
| Teams (Multi-Tenant) | ✅ | teams.md |
| Wildcard Permissions | ✅ | wildcard-permissions.md |
| Super Admin | ✅ | super-admin.md |
| Cache Management | ✅ | cache.md |
| Direct vs Role Perms | ✅ | direct-permissions.md |
| Artisan Commands | ✅ | artisan-commands.md |
| UUID Support | ✅ | custom-models.md |
| Custom Models | ✅ | custom-models.md |
| Events (v6.15.0+) | ✅ | events.md |
| Query Scopes | ✅ | query-scopes.md |
| Policy Integration | ✅ | policies.md |
| API / Sanctum | ✅ | api-usage.md |
| Testing | ✅ | testing.md |
| Performance | ✅ | performance.md |
Laravel 13 Notes
Spatie Permission 8.x (current 8.3, requires PHP 8.3+ and Laravel 12/13) is Laravel 13 compatible. L13 integrations:
- Upgrade v6 → v7 → v8: event classes suffixed
Event (RoleAttachedEvent…), commands suffixed Command, clearClassPermissions() removed, findByName() / findOrCreate() accept BackedEnum|string (v8) — see events.md
- Cache: the
permission.cache.key key stays explicit (spatie.permission.cache) in config/permission.php → unaffected by L13's switch to hyphenated prefixes
- Controller attributes: combine
#[Authorize] (policy) and #[Middleware('role:admin')] (RBAC), namespace Illuminate\Routing\Attributes\Controllers — see [[laravel-blade]]
- PHP 8.3:
final readonly class for custom Role/Permission DTOs
#[Middleware(['auth', 'role:admin|editor'])]
#[Authorize('update', 'post')]
public function update(UpdatePostRequest $request, Post $post) { /* ... */ }
Best Practices
DO
- Define a single source of truth: Policy OR Permission (never both for the same action)
- Use
permission:create-post (verb-noun) for clarity
- Enable
teams only for genuine multi-tenancy
- Cache permissions per user (
Cache::remember('user.permissions.'.$id, ...))
- Super admin via
Gate::before() (never via a * wildcard permission)
DON'T
- Store permissions in the session (use the Spatie cache)
- Mix
hasRole() and hasPermissionTo() without a consistent architecture
- Hardcode role names in code (use a
RoleEnum enum)
- Forget to flush the cache after
assignRole() in a seeder
- Grant
* to a super admin (prefer a targeted Gate::before())