Master multi-tenant security by implementing robust row-level isolation in Laravel. Learn to build tenant-aware Eloquent scopes that prevent cross-tenant leaks.
Previously in this course, we explored JWT and Stateless Security to manage identity. Now, we must ensure that once a user is authenticated, they cannot access data belonging to another customer. In a shared-database architecture, "security by convention" is insufficient; you need a system that enforces isolation at the database query level.
In a multi-tenant application using a shared database, every table typically contains a tenant_id column. The primary risk is a developer forgetting to add ->where('tenant_id', $currentTenantId) to a query, which exposes sensitive information to unauthorized users.
We solve this by moving from explicit filtering to Global Scopes. By binding the tenant context to the application lifecycle, we force Eloquent to append the necessary constraints to every SELECT, UPDATE, and DELETE operation automatically.
To implement this, we first need a contract or a trait that defines which models are "tenant-sensitive."
PHPnamespace App\Traits; use App\Models\Scopes\TenantScope; trait BelongsToTenant { protected static function bootBelongsToTenant() { static::addGlobalScope(new TenantScope); static::creating(function ($model) { if (is_null($model->tenant_id)) { $model->tenant_id = auth()->user()?->tenant_id; } }); } }
The TenantScope class then performs the heavy lifting by modifying the query builder instance:
PHPnamespace App\Models\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class TenantScope implements Scope { public function apply(Builder $builder, Model $model) { #6A9955">// Fallback to a service or request-bound tenant provider $tenantId = app('current_tenant_id'); if ($tenantId) { $builder->where($model->getTable() . '.tenant_id', $tenantId); } } }
In our ongoing project, we need a reliable way to resolve the current_tenant_id. Relying directly on auth() inside a scope can be fragile (e.g., during background jobs). Instead, we use a dedicated TenantManager service that is set during the request cycle via middleware.
TenantManager::setTenant($id).TenantScope now pulls from app(TenantManager::class)->getTenantId().This architecture ensures that even if you forget to filter in your controller, the query will fail or return empty rather than leaking data. As discussed in Preventing BOLA Vulnerabilities in Multi-Tenant API Architectures, this is your primary defense against Broken Object Level Authorization (BOLA).
TenantScope class that filters by tenant_id.BelongsToTenant trait to your Project and Task models.Task by ID where the tenant_id does not match the currently set tenant. Assert that the result is null or a ModelNotFoundException.withoutTenant() method in your scope to allow administrative access for cross-tenant reporting when explicitly required.tenant_id into a queued job, the TenantManager will be empty, and your jobs will fail to find any records. Always serialize the tenant_id into your job constructor.Model::withoutGlobalScope(TenantScope::class)->get() is the standard way to bypass the filter, but ensure this is wrapped in strict authorization logic.tenant_id column is included in every composite index. As we covered in Advanced Indexing Strategies, filtering on an unindexed column will lead to full table scans as your tenant count grows.Security in multi-tenancy is not an "add-on"—it must be baked into the Eloquent layer. By using Global Scopes, we eliminate the possibility of human error in controller-level queries. While this provides strong isolation, remember that it is only one layer of defense; you should still audit your API endpoints for BOLA risks as outlined in our security roadmap.
For environments requiring even stricter isolation, consider exploring Postgres RLS for Multi-Tenant SaaS, which moves the enforcement from the application layer down to the database engine itself.
Up next: We will discuss how to prevent Server-Side Request Forgery (SSRF) when your application needs to fetch resources from external URLs on behalf of different tenants.
Mass assignment is a critical security vulnerability where attackers inject unauthorized fields into your database. Learn to harden your Laravel models today.
Read moreLearn to implement robust multi-tenancy in Laravel. Master tenant scoping and data isolation strategies to build secure, scalable, and maintainable SaaS platforms.
Multi-Tenant Security Isolation
Custom Middleware Development
Database Connection Pooling
Handling Large Data Exports
Security Header Configuration
Database Sharding Concepts
Real-time Data Synchronization
Database Deadlock Prevention
Managing Third-Party API Integrations