Tenant Session Store
Component Detail
Description
Persistence layer that reads and writes the selected organization ID and associated tenant metadata to both secure local storage (for offline resumption) and Supabase session metadata (for server-side RLS enforcement). Ensures the tenant context survives app restarts and is available to Supabase RLS policies from the moment of selection.
tenant-session-store
Summaries
The Tenant Session Store is the persistence backbone of the organization selection experience, ensuring that a user's chosen organizational context is retained across app restarts, device sleeps, and network interruptions. Without this component, users would be forced to reselect their organization every time they open the app — a significant friction point that degrades daily active usage and increases abandonment rates in B2B mobile workflows. Beyond user convenience, this component also plays a direct role in backend security enforcement: by writing the selected organization ID into Supabase session metadata, it enables Row-Level Security policies on the server side to automatically filter all database queries to the correct tenant — making data isolation a built-in guarantee rather than an application-layer concern that developers must remember to enforce manually.
The Tenant Session Store is a medium-complexity infrastructure component with a direct dependency on the secure-storage-adapter. It introduces dual-write complexity — data must be persisted to both local secure storage and Supabase session metadata — which means testing must cover partial failure scenarios (e.g., local write succeeds but Supabase session update fails) and define a clear recovery strategy. This component is on the critical path for both app startup (context restoration) and logout/org-switch flows (context clearing), so regressions here can silently break RLS enforcement on the backend — a high-severity risk. Testing requirements include: org context round-trip (save + load), app restart persistence, org switch invalidation, logout clearing, and Supabase session metadata propagation verification with a backend engineer.
Coordinate session metadata field naming with the backend team before implementation to avoid RLS policy mismatches.
The Tenant Session Store implements a dual-persistence pattern: `saveSelectedOrg` writes the org ID and `TenantContext` struct to the secure-storage-adapter (likely Flutter Secure Storage or similar) AND calls `updateSupabaseSessionMetadata` to inject the org ID as a custom claim into the Supabase JWT session. This custom claim is what Supabase RLS policies read on every database query to enforce tenant isolation — making this component security-critical. On app startup, `loadSelectedOrg` and `loadTenantContext` are called to restore context before the route guard evaluates navigation. `clearSession` must atomically clear both local storage and Supabase session claims on logout or org switch to prevent stale-tenant data from leaking into subsequent sessions.
Use `flutter_secure_storage` for the local persistence layer and Supabase's `auth.updateUser` or session metadata APIs for server-side claim injection. Consider wrapping both writes in a try/catch with a rollback strategy if the Supabase update fails.
Responsibilities
- Write selected org ID to secure local storage
- Write org context metadata to Supabase session custom claims
- Read persisted org ID on app startup for context restoration
- Clear stored org context on logout or org switch
- Provide typed access to stored tenant metadata
Interfaces
saveSelectedOrg(String orgId, TenantContext context) -> Future<void>
loadSelectedOrg() -> Future<String?>
loadTenantContext() -> Future<TenantContext?>
clearSession() -> Future<void>
updateSupabaseSessionMetadata(String orgId) -> Future<void>