今日已更新 339 条资讯 | 累计 19899 条内容
关于我们

Resolve the tenant from the user, not the request

Nasrul Hazim Bin Mohamad 2026年06月30日 08:04 3 次阅读 来源:Dev.to

TL;DR A multi-tenant app was resolving the active tenant from the request (subdomain/header) instead of the authenticated user . That makes the client the source of truth for "which tenant am I" — the wrong place for it. Fix: derive the tenant from the user's organization membership, enforce it in middleware, and fail closed. One test locks the behaviour. The bug, in one sentence The request was telling the app which tenant to load, and the app believed it. In a multi-tenant SaaS, every query is implicitly scoped: "give me this tenant's dashboards." If the tenant ID comes from something the client controls — a subdomain, a header, a route param — then the scoping is only as trustworthy as the client. That's a leak waiting to happen. Where the trust should live Think of it like a building pass. The request is someone saying "I'm here for floor 9." The membership record is the pass that says which floors you're actually allowed on. You check the pass, not the claim. Before After Source of truth request (subdomain / header) user's organization membership Who decides the tenant the client the server Failure mode user can land in a tenant they don't belong to resolution fails closed Testable? hard — depends on request shape yes — depends on the user The shape of the fix Resolve the tenant from the authenticated user's organization, in one middleware, before anything tenant-scoped runs: final class SetTenantContext { public function handle ( Request $request , Closure $next ): Response { $org = $request -> user () ?-> currentOrganization (); // No org, no tenant context. Fail closed, never guess. abort_if ( $org === null , 403 , 'No organization context.' ); Tenancy :: setCurrent ( $org -> tenant ); // server-derived, not request-derived return $next ( $request ); } } The key line isn't the setCurrent() — it's that the value comes from $request->user() , not from $request . The user is authenticated; the subdomain is not. request ──> [auth] ──> [SetTenantContext] ──> tena

本文内容来源于互联网,版权归原作者所有
查看原文