Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Tarantula's avatar

Filament login with multiple databases

Hi everyone, I am creating SaaS project and trying to use filament for my admin panel. As you know, SaaS project has a single code base with multiple databases. The whole project is written as a single database, there is no customization in any of my models.

My solution should be something like this

I create a MASTER database that is default and create a companies table that puts all customers. With the login request, I switch all databases by companies table and find the user to which company it belongs and switch to this database finally, create the token with database name in payload and go!

Another middleware to check the valid token and also to switch in the corresponding database and go!

But this solutions doesn't work.

My Login customization

public function authenticate(): ?\Filament\Http\Responses\Auth\Contracts\LoginResponse
{
    $databases = DB::table('companies')->get();
    $credentials = $this->getCredentialsFromFormData($this->form->getState());
    $userService = new UserService();

    foreach ($databases as $database) {
        Config::set('database.connections.mysql.database', $database->database);
        DB::reconnect('mysql');
        $user_found = $userService->getUserByEmail($credentials['email']);
        if ($user_found) {
            $response = parent::authenticate();
            Session::put('company', $database);
            return $response;
        }
    }

    Filament::auth()->logout();
    $this->throwFailureValidationException();
}

My middleware

public function handle(Request $request, Closure $next)
{
    if(auth()->check()) {
        $company = session('company');
        if ($company) {
            Config::set('database.connections.mysql.database', $company->database);
            DB::reconnect('mysql');
            return $next($request);
        }
        return redirect('/');
    }

    return $next($request);
}

When user isn't authenticated, automatically redirected to the dashboard with empty values. I don't have an idea why is happend

0 likes
10 replies
experimentor's avatar

The logic is way too complex for me. But I think your middleware may need fixing.

if auth()->check() fails, should you not throw an error or redirect to login page?

Tarantula's avatar

@experimento Thank you for your reply, do you have any idea to simplify this logic single codebase with multiple databases ?

When I redirect the login page I got an error The page isn’t redirecting properly

experimentor's avatar

@Tarantula About the redirection error: Please check that your Authentication routes are public and not blocked by Auth Middleware. Here is a reference link for this issue: https://laracasts.com/discuss/channels/general-discussion/the-page-isnt-redirecting-properly

About helping you with logic for multiple dynamic databases, that is beyond my capabilities. I have never used dynamic switching databases in Laravel. I also recommend @tray2 's advice.

Tray2's avatar

Why are you using multiple databases, is each tenants database hundred of millions of rows, it not stick with a single database, and make sure to have a tenant_id in the tables where needed. That approach will save you lots of headache.

Tarantula's avatar

@Tray2 Thank you for your reply, but my bussiness logic is more complicated than add a tenant_id in every table. Do you have any idea about why the middleware doesn't work properly in this case ?

Tray2's avatar

@Tarantula Either your auth check fails cause you don't have a logged in user, or it fails because you don't have a company set in the session.

I still don't see any complexity that couldn't be solved in a single database. If it's security concerns, then I would setup subdomains, and a database for each subdomain, and a application instance for each as well.

Tarantula's avatar

@Tray2 there are some reasons that I use this logic, for example as you mentioned security, restore backups, imports, accessibility from some third parties and so. But this logic is not very complicated as you said. I created the solution about that in my API services, I use as subdomain with different branch for my mobile applications.

The solution is

public function login(Request $request)
{

    $credentials = $request->only('email', 'password');

    $loginService = new LoginService();

    $databases = LoginService::getDatabases($credentials);

    foreach ($databases as $database) {
        if (!GlobalHelper::databaseExist($database->database_name)) {
            continue;
        }

        config(['database.connections.mysql.database' => $database->database_name]);
        DB::purge('mysql'); // Clear the previous connection
        DB::reconnect('mysql'); // Reconnect to the new database
        if ($loginService->attempt($credentials)) {
            $user = UserService::getUserByEmail($credentials['email']);
            Auth::login($user);
            $token = JWTHelper::encode([
                'id' => auth()->user()->id,
                'exp' => time() + 28800,
                'company' => $database
            ]);
            return $this->sendResponse(['token' => $token]);
        }
    }

    return $this->sendError(__('messages.incorrect_login'), '', 404);
}

And for other request I use this middleware

public function handle(Request $request, Closure $next): Response
{
	$token = $request->bearerToken();					
     if (!$token) {
        return response()->json(['error' => 'Token not provided'], 401);
    }
    try {
        $decoded = JWTHelper::decode($token);
        $database = $this->getDatabase($decoded->database);
        config(['database.connections.mysql.database' => $database_name]);
        DB::purge('mysql'); // Clear the previous connection
        DB::reconnect('mysql'); // Reconnect to the new database
        Auth::login(UserService::getUserById($decoded->id));
        $request->merge((array)$decoded);
    } catch (\Exception $e) {
        return response()->json(['error' => $e->getMessage()], 401);
    }

    return $next($request);

}

entire project work perfectly, but this logic doesn't work in filament, and I don't know why. In filament the token doesn't exist and I put the database in session and check it in future request in middleware, but sessions doesn't exist when arrived in middleware? I check also Filament::auth()->user() also doesn't exist, but the login process finished successfully. Is it terminated somewhere ? Maybe there are some other logic behind the filament that I don't know, because in normal conditions I will able to check Filament::auth()->user() in middleware after login also redirect in login page correctly. I think there is an issue about order of middleware

Please or to participate in this conversation.