Implement cache invalidation on logout
epic-role-based-access-control-data-infrastructure-task-006 — Wire the RoleRepository's cache invalidation logic to the auth session lifecycle so that all cached role assignments are cleared when the user logs out or the session expires. Integrate with the existing Supabase auth state stream (onAuthStateChange) to trigger invalidation automatically without requiring explicit calls from the UI layer.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Listen to `Supabase.instance.client.auth.onAuthStateChange` in the RoleRepository constructor. Store the StreamSubscription and cancel it in a dispose() method. The handler should check `event == AuthChangeEvent.signedOut || (event == AuthChangeEvent.tokenRefreshed && session == null)` before clearing. Avoid calling clearCache() on every AuthChangeEvent — only on session-ending events.
Since RoleRepository is typically a long-lived singleton in Riverpod, ensure the subscription lifetime matches the repository lifetime. Do not couple this to any UI lifecycle widget. Use a private `_authSubscription` field. Consider a thin `AuthSessionObserver` helper if multiple repositories need the same pattern — but only if it already exists or is needed by another task in the epic.
Testing Requirements
Unit tests: mock the Supabase auth stream and verify that emitting AuthChangeEvent.signedOut triggers RoleRepository.clearCache(). Verify that emitting AuthChangeEvent.tokenRefreshed with a null session also triggers clearCache(). Verify that valid token refresh (non-null session) does NOT clear cache. Integration test: simulate full login → cache population → logout cycle against local Supabase instance and assert cache is empty post-logout.
Test that a second login after logout does not serve stale data from the first session. Verify StreamSubscription is cancelled on repository dispose — use a subscription spy. Target 90%+ branch coverage on the auth-listener logic.
The get_my_roles RPC call adds a network round-trip immediately after login, potentially increasing the time before the home screen renders. If Supabase RPC is slow or the roles table lacks proper indexing, users with multiple org affiliations could experience noticeable delays.
Mitigation & Contingency
Mitigation: Index user_roles on user_id and org_unit_id. Use JWT claim extraction as the primary fast path; fall back to the RPC only when claims are absent or stale. Set a 3-second timeout with a fallback to cached roles.
Contingency: If RPC latency exceeds acceptable thresholds in production, pre-fetch and embed roles into the session JWT at login time via a Supabase Auth hook, eliminating the post-login RPC entirely.
Users who belong to multiple organizations (e.g., a coordinator in one NHF chapter who is also a peer mentor in another) may have conflicting role assignments. The repository layer must correctly scope roles to the active organization context set during the organization selection step, or it could return roles from the wrong org.
Mitigation & Contingency
Mitigation: Always filter role queries by the active org_unit_id stored in the tenant session. Write integration tests that simulate multi-org users and verify only the correct org's roles are returned.
Contingency: If org-scoping logic is found to be incorrect during QA, add an explicit org_unit_id parameter to get_my_roles RPC and require the client to always pass the active org context, making the scoping explicit rather than inferred.