When building multi-tenant SaaS applications in Flutter across Mobile, Web, and Desktop, ensuring zero tenant data leakage is the foundational engineering requirement.

Rather than relying on client-side state or middleware checks alone, a bulletproof architecture enforces tenant boundaries directly inside PostgreSQL using Row Level Security (RLS) coupled with Supabase JWT claims.

JWT Custom Claims and Tenant Context

Every authenticated request in Flutter includes a cryptographically signed JWT. By embedding the user's active tenant ID into the JWT app metadata, PostgreSQL can extract this value on every database operation without client manipulation.

-- Supabase PostgreSQL RLS Policy
CREATE POLICY tenant_isolation_policy ON orders
FOR ALL
USING (
  tenant_id = (auth.jwt() -> 'app_metadata' ->> 'tenant_id')::uuid
)
WITH CHECK (
  tenant_id = (auth.jwt() -> 'app_metadata' ->> 'tenant_id')::uuid
);

Structuring the Flutter Clean Architecture Layer

In the Flutter client, tenant resolution is handled by a dedicated TenantSessionService at the application boundary. Repositories consume Supabase clients configured with the current session token.

  • Tenant session injected into Repository scopes via Riverpod or GetX
  • Automatic re-querying and cache eviction upon switching tenant context
  • Offline draft storage partitioned by tenant ID using Hive encrypted boxes

Handling Kiosk and Role-Based Permissions

In tailoring and retail environments, floor kiosks need restricted access compared to back-office admins. We map roles into database policies so that kiosk devices can only insert measurements and read queue status without exposing tenant financial records.

Closing perspective

By pushing tenancy enforcement to PostgreSQL RLS, the Flutter client remains focused on UI responsiveness and clean state transitions without security burden.