iprote / saas-auth
Multi-tenant role and permission management for Laravel SaaS applications
Requires
- php: ^8.1
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0
This package is auto-updated.
Last update: 2025-06-12 08:34:53 UTC
README
A Laravel package for multi-tenant role and permission management, perfect for SaaS applications where users can belong to multiple tenants with different roles in each.
Features
- 🏢 Multi-tenant architecture - Users can belong to multiple tenants
- 🎭 Tenant-scoped roles - Different roles per tenant for the same user
- 🔐 Permission management - Fine-grained permissions within each tenant
- 🛡️ Middleware protection - Route-level tenant permission/role checking
- 🎨 Blade directives - Template-level authorization checks
- ⚡ Easy installation - One command setup
- 🔧 Configurable - Customizable table names and user models
- ⚛️ Inertia.js + React support - Modern frontend integration
Installation
Install the package via Composer:
composer require iprote/saas-auth
Publish and run the migrations:
php artisan saas-auth:install php artisan migrate
Add the HasTenantRoles
trait to your User model:
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Iprote\SaasAuth\Traits\HasTenantRoles; class User extends Authenticatable { use HasTenantRoles; // ... rest of your User model }
Basic Usage
Creating Tenants, Roles, and Permissions
use Iprote\SaasAuth\Models\Tenant; use Iprote\SaasAuth\Models\Role; use Iprote\SaasAuth\Models\Permission; // Create a tenant $tenant = Tenant::create([ 'name' => 'Acme Corporation', 'slug' => 'acme-corp' ]); // Create permissions $editPermission = Permission::create(['name' => 'edit-posts']); $deletePermission = Permission::create(['name' => 'delete-posts']); // Create a role for the tenant $editorRole = Role::createForTenant('editor', $tenant); // Assign permissions to the role $editorRole->givePermissionTo($editPermission); $editorRole->givePermissionTo($deletePermission);
Assigning Users to Tenants
// Assign user to tenant with a role $user->assignRoleToTenant($editorRole, $tenant); // Or using the facade use Iprote\SaasAuth\Facades\SaasAuth; SaasAuth::assignUserToTenant($user, $tenant, $editorRole);
Checking Permissions and Roles
// Check if user has permission in tenant if ($user->hasPermissionInTenant('edit-posts', $tenant)) { // User can edit posts in this tenant } // Check if user has role in tenant if ($user->hasRoleInTenant('editor', $tenant)) { // User is an editor in this tenant } // Get user's role in tenant $role = $user->getRoleInTenant($tenant); // Get user's permissions in tenant $permissions = $user->getPermissionsInTenant($tenant);
Middleware
Protect your routes with tenant-aware middleware:
// Require specific permission in tenant Route::middleware(['auth', 'tenant_can:edit-posts,tenant'])->group(function () { Route::put('/tenants/{tenant}/posts/{post}', [PostController::class, 'update']); }); // Require specific role in tenant Route::middleware(['auth', 'tenant_role:editor,tenant'])->group(function () { Route::get('/tenants/{tenant}/dashboard', [DashboardController::class, 'index']); });
The middleware expects the tenant to be passed as a route parameter. You can customize the parameter name:
// Use 'organization' instead of 'tenant' Route::middleware(['tenant_can:edit-posts,organization'])->group(function () { Route::put('/orgs/{organization}/posts/{post}', [PostController::class, 'update']); });
Blade Directives
Use Blade directives for template-level authorization:
@tenantCan('edit-posts', $tenant) <a href="{{ route('posts.edit', [$tenant, $post]) }}">Edit Post</a> @endTenantCan @tenantRole('admin', $tenant) <a href="{{ route('tenant.settings', $tenant) }}">Tenant Settings</a> @endTenantRole
Inertia.js + React Support
For modern frontend applications using Inertia.js with React, the package provides seamless integration with React components and hooks. See the Inertia React Guide for detailed setup and usage examples.
// React Permission Guard Component <PermissionGuard permission="edit-posts"> <button onClick={handleEdit}>Edit Post</button> </PermissionGuard> // React Permission Hook const { hasPermission, hasRole } = usePermissions(); if (hasPermission('edit-posts')) { // User can edit posts }
API Reference
User Methods (via HasTenantRoles trait)
// Tenant relationships $user->tenants; // Get all tenants for user $user->belongsToTenant($tenant); // Check if user belongs to tenant // Role management $user->assignRoleToTenant($role, $tenant); $user->removeRoleFromTenant($role, $tenant); $user->removeFromTenant($tenant); // Role checking $user->hasRoleInTenant($roleName, $tenant); $user->hasAnyRoleInTenant(['admin', 'editor'], $tenant); $user->hasAllRolesInTenant(['editor', 'publisher'], $tenant); $user->getRoleInTenant($tenant); // Permission checking $user->hasPermissionInTenant($permissionName, $tenant); $user->hasAnyPermissionInTenant(['edit', 'delete'], $tenant); $user->hasAllPermissionsInTenant(['read', 'write'], $tenant); $user->getPermissionsInTenant($tenant); // Utility methods $user->getAllTenantRoles(); // Get roles across all tenants $user->getTenantsWithRole($roleName); $user->getTenantsWithPermission($permissionName);
Tenant Methods
$tenant->users; // Get all users in tenant $tenant->roles; // Get all roles for tenant $tenant->addUser($user, $role); // Add user with optional role $tenant->removeUser($user); // Remove user from tenant $tenant->hasUser($user); // Check if user belongs to tenant $tenant->getUserRole($user); // Get user's role in tenant
Role Methods
$role->tenant; // Get tenant that owns the role $role->permissions; // Get all permissions for role // Scopes Role::forTenant($tenant)->get(); // Get roles for specific tenant Role::global()->get(); // Get global roles (no tenant) // Permission management $role->givePermissionTo($permission); $role->revokePermissionTo($permission); $role->hasPermissionTo($permission); $role->hasPermission($permissionName); $role->syncPermissions($permissions); // Factory method Role::createForTenant($name, $tenant, $attributes);
Permission Methods
$permission->roles; // Get all roles that have this permission // Factory methods Permission::findByName($name, $guardName); Permission::findOrCreate($name, $guardName); // Scopes Permission::forTenant($tenant)->get(); // Get permissions used in tenant
Facade Usage
use Iprote\SaasAuth\Facades\SaasAuth; // Create entities $tenant = SaasAuth::createTenant('Company Name'); $role = SaasAuth::createRole('manager', $tenant); $permission = SaasAuth::createPermission('manage-users'); // User management SaasAuth::assignUserToTenant($user, $tenant, $role); SaasAuth::userHasPermissionInTenant($user, 'manage-users', $tenant); SaasAuth::userHasRoleInTenant($user, 'manager', $tenant); // Utility methods $tenants = SaasAuth::getUserTenants($user); $role = SaasAuth::getUserRoleInTenant($user, $tenant);
Configuration
Publish the config file to customize the package:
php artisan vendor:publish --tag=saas-auth-config
The config file allows you to customize:
- User model class
- Table names
- Column names
- Cache settings
- Middleware aliases
// config/saas-auth.php return [ 'user_model' => App\Models\User::class, 'table_names' => [ 'tenants' => 'tenants', 'roles' => 'roles', 'permissions' => 'permissions', 'role_has_permissions' => 'role_has_permissions', 'tenant_user' => 'tenant_user', ], // ... other configuration options ];
Database Schema
The package creates the following tables:
tenants
- Stores tenant informationpermissions
- Stores available permissionsroles
- Stores roles (with optional tenant_id for scoping)role_has_permissions
- Pivot table for role-permission relationshipstenant_user
- Pivot table for user-tenant relationships with role assignment
Advanced Usage
Global vs Tenant Roles
You can create global roles (not tied to any tenant) by omitting the tenant:
// Global role (works across all tenants) $globalAdmin = Role::create([ 'name' => 'super-admin', 'tenant_id' => null ]); // Tenant-specific role $tenantAdmin = Role::createForTenant('admin', $tenant);
Custom User Model
If you're using a custom User model, update the config:
// config/saas-auth.php 'user_model' => App\Models\CustomUser::class,
Route Model Binding
The middleware works seamlessly with route model binding:
// routes/web.php Route::middleware(['tenant_can:edit-posts'])->group(function () { Route::put('/tenants/{tenant}/posts/{post}', [PostController::class, 'update']); }); // In your controller public function update(Tenant $tenant, Post $post) { // The middleware has already verified the user has 'edit-posts' permission in this tenant // Your update logic here }
Frontend Integration
Traditional Blade Templates
Use the provided Blade directives for server-side rendering:
@tenantCan('edit-posts', $tenant) <a href="{{ route('posts.edit', [$tenant, $post]) }}">Edit Post</a> @endTenantCan
Inertia.js + React
For modern SPAs, use the React components and hooks:
import PermissionGuard from '@/Components/PermissionGuard'; import usePermissions from '@/Hooks/usePermissions'; function ProjectCard({ project, tenant }) { const { hasPermission } = usePermissions(); return ( <div> <h3>{project.name}</h3> <PermissionGuard permission="edit-projects"> <button>Edit</button> </PermissionGuard> </div> ); }
See the complete Inertia React Guide for detailed examples.
Documentation
- Installation & Basic Usage - This file
- Inertia.js + React Integration - Complete frontend guide
- Usage Examples - Real-world scenarios
- Changelog - Version history
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Security
If you discover any security related issues, please email the maintainers instead of using the issue tracker.
License
The MIT License (MIT). Please see License File for more information.
Credits
- iProte - Package development
- Inspired by Spatie Laravel-Permission
Support
If you find this package useful, please consider starring the repository. For support questions, please use the GitHub issues.