euacreations / laravel-iam
IAM / RBAC package for Laravel
Requires
- php: ^8.2
- illuminate/support: ^11|^12
Requires (Dev)
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0
README
Feature-based Identity & Access Management for Laravel
Overview
Laravel IAM is a feature-first, action-driven authorization system for Laravel. It focuses on capabilities (Features) instead of arbitrary permission labels, ensuring that system capabilities are always in sync with your codebase.
Key Principles
- Users have exactly one role
- Features correspond to actual system actions (controllers/routes)
- Admin role automatically has all features
- Automatic sync for new features added to the system
Core Concepts
| Concept | Description |
|---|---|
| Feature | A concrete system action, e.g., subscriber.create |
| Role | A bundle of features assigned to a user |
| User | A single identity that authenticates using username (email optional) |
| Admin | Built-in user with all features and root privileges |
Features
- ✅ Feature-based authorization (action-driven)
- ✅ Username-first authentication
- ✅ Single role per user
- ✅ Automatic feature discovery from routes
- ✅ Automatic feature assignment to Admin and roles with auto-assign enabled
- ✅ Laravel Gate & Blade integration
- ✅ Framework-agnostic, no UI
- ✅ Multi-provider authentication support (local, OAuth)
Installation
Install via Composer:
composer require euacreations/laravel-iam
Publish config (optional):
php artisan vendor:publish --tag=iam-config
Run migrations:
php artisan migrate
Minimal Installation Requirements
Before running iam:install, ensure:
- At least one user exists in your application.
- You can verify with:
php artisan iam:user-list
- You can verify with:
- Your
userstable has arole_idcolumn. - Your
Usermodel uses the IAM trait.
If no users exist, iam:install will stop and ask you to create the first user.
Add role_id to Users
Create a migration to add the role_id column:
Schema::table('users', function (Blueprint $table) { $table->foreignId('role_id')->nullable()->constrained('iam_roles'); });
Add IAM Trait to User
use EuaCreations\LaravelIam\Traits\HasFeatures; class User extends Authenticatable { use HasFeatures; public function role() { return $this->belongsTo(\EuaCreations\LaravelIam\Models\Role::class); } }
Quick Start
1. Define Features in Routes
Features are automatically defined from routes using middleware. When you use
feature without a parameter, the route name becomes the feature slug.
Route::post('/subscriber', [SubscriberController::class, 'store']) ->name('subscriber.create') ->middleware('feature'); Route::delete('/subscriber/{id}', [SubscriberController::class, 'destroy']) ->name('subscriber.delete') ->middleware('feature');
You can still pass an explicit slug:
Route::post('/subscriber', [SubscriberController::class, 'store']) ->middleware('feature:subscriber.create');
2. Sync Features
Run the Artisan command to discover and register features:
php artisan iam:sync-features
This will:
- Register missing features from your routes
- Automatically assign them to Admin
- Assign to roles with
auto_assign_new_features = true
3. Create Roles and Assign Features
use EuaCreations\LaravelIam\Models\Role; use EuaCreations\LaravelIam\Models\Feature; // Create a role $role = Role::create([ 'name' => 'Content Manager', 'auto_assign_new_features' => false ]); // Assign specific features to role $createFeature = Feature::where('slug', 'subscriber.create')->first(); $role->features()->attach($createFeature); // Assign user to role $user->update(['role_id' => $role->id]);
4. Check Authorization
Use Laravel's native authorization methods:
// In controllers if (Gate::allows('subscriber.delete')) { // User can delete subscribers } // In Blade templates @can('subscriber.create') <button>Create Subscriber</button> @endcan // Using middleware Route::delete('/subscriber/{id}', [SubscriberController::class, 'destroy']) ->name('subscriber.delete') ->middleware('feature');
Database Schema
users
└─ role_id (foreign key)
roles
├─ name
└─ auto_assign_new_features (boolean)
features
├─ slug (e.g., subscriber.create)
├─ name
└─ description
role_has_features (pivot)
├─ role_id
└─ feature_id
Key Design Decisions:
- One-to-many relationship between Role and User (single role per user)
- Many-to-many relationship between Role and Feature (flexible capability assignment)
- Admin user automatically bypasses all feature checks
Authorization Usage
Middleware
Protect routes with feature-based middleware:
Route::post('/subscriber', [SubscriberController::class, 'store']) ->name('subscriber.create') ->middleware('feature');
Global Enforcement (Optional)
You can enforce IAM for all named routes by enabling global enforcement:
IAM_ENFORCE_GLOBAL=true
When enabled:
- All named routes will be checked using their route name as the feature slug
- Routes with
featuremiddleware are respected as-is - You can opt out per route using
skip-feature
Example opt-out:
Route::get('/health', fn () => 'ok') ->middleware('skip-feature');
Gates
Check permissions programmatically:
if (Gate::allows('subscriber.delete')) { // Authorized } Gate::authorize('subscriber.update');
Blade Directives
Conditionally render UI elements:
@can('subscriber.delete') <button class="btn-danger">Delete</button> @endcan @cannot('subscriber.edit') <p>You don't have permission to edit subscribers.</p> @endcannot
Policy Integration
Works seamlessly with Laravel Policies:
// In SubscriberPolicy public function delete(User $user, Subscriber $subscriber) { return Gate::allows('subscriber.delete'); }
Authentication vs Authorization
Laravel IAM assumes you already authenticate users using Laravel’s auth system (Breeze, Jetstream, Fortify, or custom auth). This package only adds authorization (roles and feature access), and does not implement login, registration, or password management.
Testing
Run the test suite:
composer test
Current Test Coverage
The package includes a growing test suite using Pest
- ✅ Feature discovery and sync logic
- ✅ Role-feature assignment rules
- ✅ Admin privilege enforcement
- ✅ User authentication flows
- ✅ Gate and policy integration
- ✅ Auto-assignment behavior
Running Specific Tests
# Run feature sync tests ./vendor/bin/pest --filter=FeatureSyncTest # Run authorization tests ./vendor/bin/pest --filter=AuthorizationTest
Architecture & Design Patterns
Laravel IAM follows domain-driven design principles and implements several proven patterns:
Design Patterns
- Repository Pattern: Clean data access layer for models
- Strategy Pattern: Multiple authentication providers (local, OAuth)
- Observer Pattern: Auto-sync and auto-assignment of features
- Factory Pattern: Feature creation from route definitions
- Policy Pattern: Integration with Laravel's authorization system
Domain-Driven Design
- Bounded Context: Authorization and authentication as separate concerns
- Aggregate Root: Role as the central entity for capability management
- Value Objects: Feature keys as immutable identifiers
- Domain Events: Feature sync triggers role updates
Database Design
- Normalized schema with proper foreign key constraints
- Pivot table for many-to-many relationships
- Indexed columns for performance (feature keys, role IDs)
- Soft deletes support for audit trails
Configuration
Publish and customize the configuration file:
php artisan vendor:publish --tag=iam-config
Available Options
return [ // Database table names 'tables' => [ 'users' => 'users', 'roles' => 'roles', 'features' => 'features', 'role_has_features' => 'role_has_features', ], ];
Console Commands
Core
php artisan iam:install php artisan iam:sync-features php artisan iam:feature-prune
Roles
php artisan iam:role-create {slug} {name}
php artisan iam:role-assign {user} {role}
php artisan iam:role-feature {role} {features}
php artisan iam:role-list
php artisan iam:role-show {role}
Features
php artisan iam:feature-create {slug} {name}
php artisan iam:feature-list
Users
php artisan iam:user-list
Philosophy
Laravel IAM follows a capability-first model, similar to enterprise IAM systems:
- AWS IAM: Actions → Policies → Roles → Users
- Kubernetes RBAC: Verbs → Rules → Roles → Subjects
- OAuth 2.0: Scopes → Clients → Users
Core Philosophy:
Capabilities → Roles → Users
Instead of asking "What permissions does this user have?", we ask:
"What features can this user's role access?"
This ensures:
- Type Safety: Features correspond to actual code (routes/controllers)
- Maintainability: Adding a route automatically defines the capability
- Consistency: System capabilities match application capabilities
- Auditability: Clear mapping between code and authorization
Roadmap
- Core feature-based authorization
- Automatic feature discovery from routes
- Single-role-per-user model
- Laravel Gate and Blade integration
- Multi-provider authentication support
- Comprehensive test coverage expansion
- API token support (Sanctum integration)
- Service accounts (machine-to-machine auth)
- Policy bridge (Laravel policy generation)
- Audit logs and activity tracking
- Feature grouping and namespaces
- Role inheritance and hierarchies
- Time-based feature access (temporary permissions)
Use Cases
SaaS Applications
Perfect for multi-tenant SaaS where each customer needs different feature access:
// Free tier $freeRole = Role::create(['name' => 'Free User']); $freeRole->features()->attach(['post.create', 'post.read']); // Premium tier $premiumRole = Role::create(['name' => 'Premium User']); $premiumRole->features()->attach(['post.create', 'post.read', 'analytics.view', 'export.csv']);
Enterprise Applications
Manage complex organizational hierarchies:
// Department-specific capabilities $hrRole = Role::create(['name' => 'HR Manager']); $hrRole->features()->attach(['employee.create', 'employee.update', 'payroll.view']); $financeRole = Role::create(['name' => 'Finance Manager']); $financeRole->features()->attach(['invoice.create', 'payment.process', 'reports.financial']);
Compliance Systems
Track and control access to sensitive operations:
// Regulatory compliance $auditorRole = Role::create(['name' => 'Auditor', 'auto_assign_new_features' => false]); $auditorRole->features()->attach(['audit.view', 'report.generate']); // New features require explicit approval for auditors
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
# Clone the repository git clone https://github.com/euacreations/laravel-iam.git # Install dependencies composer install # Run tests composer test # Run static analysis composer analyse
License
MIT
Credits
Created by Eranga Upul Amarakoon
Inspired by enterprise IAM systems and Laravel's elegant authorization system.