In multi-tenant Laravel applications, the global scope is the tool of choice: every query of a model automatically gets the condition for the current tenant. That protects the read side well. Three places still slip past it.

1. Validation with exists and unique

The exists and unique rules query the database directly, without Eloquent. A form can thus point to a record of another tenant, and validation reports no error. Remedy: a custom rule that always checks the tenant — and a test that tries it with a foreign id.

2. Models without the scope

A tenant context that has been set is no filter if the model does not carry the scope at all — often the user model. Querying “all users of this business” then returns all users. Remedy: put exceptions into a named scope and always test lists with a second tenant that must get nothing.

3. Order of middleware

If route model binding runs before the middleware that sets the tenant, Laravel finds foreign records and the policy answers 403 instead of 404. That alone reveals that the record exists. Remedy: move the tenant before the binding via middleware priority and test “foreign means 404” in every module.

What all three cases have in common: a test with only one tenant sees no leak. Only the second tenant makes it visible.

Share

Only a click opens the network — nothing goes there before.