archtechx / tenancy

Automatic multi-tenancy for Laravel. No code changes needed.
https://tenancyforlaravel.com
MIT License
3.67k stars 432 forks source link

Session Identification #1239

Closed erlandmuchasaj closed 4 months ago

erlandmuchasaj commented 4 months ago

Description

Suppose you want to use multitenancy but without changing the URL structure. So in Path or Request identification, you need always to pass tenant_id, but with Session identification, you read it from the session.

A use case scenario:

The user lands on the application landing page (central routes). When clicks login he will go to a pre-login screen where he needs to type tenant name (could be company name).

Then we query tenants and see if the tenant exists and if yes, then we set up the _tenant_session_id=$tenant->id and redirect the user to the login screen identified with this user.

       $tenant = Tenant::query()->where('company', $request->string('company'))->first();
        if ($tenant) {
            session()->put(TenantManager::$tenantIdKey, $tenant->id);
            return redirect()->route('tenant.dashboard'); # here we will be in tenant context
        }

And a middleware could look like this:

class InitializeTenancyBySession extends IdentificationMiddleware
{
    /**
     * The key used to store the tenant id in the session.
     *
     * @var string
     */
    public static string $tenantIdKey = '_tenant_id';

    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     * @throws SessionIsMissingTenantException
     */
    public function handle(Request $request, Closure $next)
    {
        if (! session()->has(static::$tenantIdKey)) {
            throw new SessionIsMissingTenantException;
        }

        try {
            $tenantId = session()->get(static::$tenantIdKey);
        } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
            report($e);
            throw new SessionIsMissingTenantException;
        }

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

Why this should be added

This will avoid the necessity to pass on each request the tenant parameter in a stateful context.

stancl commented 4 months ago

This setup isn't really possible/a good idea. I've explained this a couple of times on our Discord.

With multi-db tenancy, you need the user sessions to be properly scoped, i.e. not be central. Otherwise you have a massive security issue. You also don't want the users to be in the central database since you wouldn't be able to relate them to data inside the tenant database.

The right way to implement "identification using the current user" is to use a specific auth flow, potentially with resource syncing.

I have some content coming up covering this use case in detail.

erlandmuchasaj commented 4 months ago

When will that content be available? So i can check it out

stancl commented 4 months ago

All updates are posted on our Discord.

erlandmuchasaj commented 4 months ago

All updates are posted on our Discord.

Where can i join to that discord?

stancl commented 4 months ago

See the project website