Why InitializeTenancyByDomain is not applicable for the login process?

Viewed 3044

This is for Laravel 8.x with Jetstream/Livewire scaffold having Stancl/Tenancy. The initialization of tenant models or session settings not working right. Either I did not do it right or inbuilt problem.

The entire package was built as per instructions of Stencl/tenancy v3.x. I can see the dd(\App\User::all()) as per code outlined below

Route::middleware([ 
        'web',
        InitializeTenancyByDomain::class,
        PreventAccessFromCentralDomains::class,     
])->group(function (){
    
Route::get('/', function () { 
   dd(\App\User::all()); //can see all users models in tenants table
   return view('welcomeTenant'); 
});

Route::get('/home', [
    'middleware'  => ['auth'],
    'uses' => '\App\Http\Controllers\HomeController@index'
])->name('home');
                
});

This meant InitializeTenancyByDomain right to me.

When a login form is requested from tenant's domain eg. from rtbs.example.in, the encrypted session/cookie info is not stored in sessions table of tenant i.e. rtbs.sessions. When a login form is posted, it is looking for users in central domain (example.in) where users table is not present, hence the central.users table not exist error. As a result I get 419 error. I had disabled the csrf token verification temporarily to identify this problem.

This is the issue. Why the InitializeTenancyByDomain is not applicable for the login process? Could there be a fundamental setting wrong with me? Interestingly, the dd(\App\User::all()) if present anywhere else i.e. as show below

Route::middleware([ 
        'web',
        InitializeTenancyByDomain::class,
        PreventAccessFromCentralDomains::class,     
])->group(function (){
    
dd(\App\User::all()); //central-domain.users does not exist error

Route::get('/', function () { 
    return view('welcomeTenant'); 
});

Route::get('/home', [
    'middleware'  => ['auth'],
    'uses' => '\App\Http\Controllers\HomeController@index'
])->name('home');
                
});

The same sql exception i.e. central-domain.users table does not exist is thrown. Only when present inside the Route::get('/'... i can see the correct models.

5 Answers

I had this problem with Laravel Fortify, which provides the backend to JetStream. It only became a problem when I changed the session driver to database from file - though this will be necessary for my deployment anyway.

The issue is that Fortify makes extensive use of dependency injection inside the constructor methods of their controllers. As described in the documentation for Tenancy, because of the lifecycle of a Laravel request the tenancy will not be available when these constructors run. This means they will default to the central connection and cause failure to login.

Because we can't change the way Fortify uses constructors, the solution is to change the middleware to be global and add a check for central domains into the initialization middleware:

Copy Stancl\Tenancy\Middleware\InitializeTenancyByDomain to App\Middleware\InitializeTenancyByDomain changing:

public function handle($request, Closure $next)
{

    //Skip for central domains
    if(in_array($request->getHost(), config('tenancy.central_domains'), true)){
        return $next($request);
    }


    return $this->initializeTenancy(
        $request, $next, $request->getHost()
    );
}

App/Http/Kernel use App\Http\Middleware\InitializeTenancyByDomain;

protected $middleware = [
    // ...
    InitializeTenancyByDomain::class,
];

Config/fortify

use Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains;

'middleware' => [
    'web',
    PreventAccessFromCentralDomains::class,
],

Routes/tenant

Route::middleware([
        'web',
        PreventAccessFromCentralDomains::class,
    ])->group(function () {

        //Your Routes

    }

With this solution Fortify and Stancl Tenancy are now working well together with well separated databases, sesssions and logins.

That package is very helpful, but it requires a lot of work to get all the pieces lined up and maintained. You've got the right middleware, including 'web' and the two tenancy bits, which will start the session and allow checks for CSRF.

I've had similar problems before. The biggest cause for me was that it was not finding the right 'central' path, and thus not initializing tenancy correctly.

Make sure your tenancy.php config file is showing the right central domain. Something like:

'central_domains' => [
     'example.in' // No http:// here
 ],

Route's like next can issue bugs with database session store

Route::view('some')

I got it to work with Rory's answer. However, you need to set the correct namespace in App\Http\Middleware\InitializeTenancyByDomain.php:

namespace App\Http\Middleware; // << use this instead of Stancl\Tenancy\Middleware

// [..]
use Stancl\Tenancy\Middleware\IdentificationMiddleware; // <<

class InitializeTenancyByDomain extends IdentificationMiddleware

You could use a feature called Universal Routes check the documentation here

Related