high priority medium complexity integration pending integration specialist Tier 6

Acceptance Criteria

After BiometricAuthSuccess, AuthRepository.currentUser is non-null and reflects the authenticated user identity
AuthRepository.authState stream emits AuthState.authenticated after biometric success
Role-based access checks (coordinator, peer mentor, org admin) function correctly after biometric re-auth without credential re-entry
AuthRepository is not bypassed — BiometricAuthService calls AuthRepository.onBiometricSuccess(session) rather than writing directly to Supabase client
If BiometricAuthService returns any failure result, AuthRepository.authState remains in its previous state (no state regression)
Integration test: full flow from biometric trigger → AuthRepository.currentUser populated → role guard allows access to protected screen
AuthRepository.signOut() clears biometric session state and re-requires full BankID/Vipps login on next session
Thread safety: concurrent calls to AuthRepository from biometric and other sources do not corrupt auth state

Technical Requirements

frameworks
Flutter
BLoC
Riverpod
apis
Supabase Auth SDK
AuthRepository (existing)
BiometricAuthService (task-005/006)
performance requirements
Auth state update must complete within 500ms of biometric success to avoid perceived lag before navigation
No redundant Supabase API calls — reuse the Session from the token refresh already performed in task-005
security requirements
Biometric auth must never grant higher privileges than the original BankID/Vipps login established
AuthRepository must validate that the refreshed JWT claims match the expected user_id before updating auth state
Session must be invalidated server-side on sign-out — not only cleared client-side
Role claims sourced from Supabase JWT — not from local storage — to prevent privilege escalation

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Define an AuthRepository.onBiometricAuthSuccess(Session session) method (or similar named contract) that accepts the refreshed session from BiometricAuthService. This keeps the repository as the single source of truth for auth state rather than having BiometricAuthService write to Supabase client directly. Use Riverpod's StateNotifier or a BehaviorSubject stream in AuthRepository to propagate auth state changes reactively to all subscribers. Ensure the existing role-based guard widgets (which presumably listen to AuthRepository) do not need modification — only the repository's internal update path changes.

After wiring, audit all downstream consumers of AuthRepository.currentUser to confirm they react correctly to biometric-sourced sessions.

Testing Requirements

Integration tests covering: biometric success updates AuthRepository.currentUser with correct User object, auth state stream emits authenticated after biometric success, role guard on a protected route passes after biometric auth, sign-out after biometric session clears all auth state. Unit test that BiometricAuthService failure paths do NOT modify AuthRepository state. Mock AuthRepository in BiometricAuthService unit tests to verify the correct method is called on success.

Component
Biometric Authentication Service
service medium
Epic Risks (3)
high impact medium prob technical

Multiple concurrent callers (e.g., SessionResumeManager and a background sync service) could simultaneously detect a near-expired token and each invoke SupabaseSessionManager.refreshSession(), causing duplicate refresh API calls and potentially a token invalidation race condition on the Supabase Auth server. This can result in one caller receiving a valid refreshed token while another receives a 401, causing intermittent authentication failures.

Mitigation & Contingency

Mitigation: Implement a single-flight pattern inside SupabaseSessionManager so that concurrent refresh calls coalesce into one in-flight request. Use a Dart Completer or AsyncMemoizer to ensure all waiters receive the same refreshed token. Write a concurrent integration test to validate the single-flight behaviour.

Contingency: If the single-flight pattern introduces deadlocks or timeout complexity, fall back to a mutex-based lock with a 10-second timeout, logging a warning if the lock is held longer than expected, and triggering a full re-login if the refresh ultimately fails.

high impact low prob security

Supabase Row-Level Security policies evaluate the JWT claims (user_id, role, org_id) on every query. If the refreshed token contains stale or changed claims — for example if a coordinator's role was updated server-side — RLS may silently block data access even though the session appears valid from the client's perspective, causing confusing empty screens rather than an authentication error.

Mitigation & Contingency

Mitigation: After every token refresh, decode the new JWT and compare key claims (role, org_id) with the cached values. If claims have changed, emit a session-claims-changed event that triggers a role re-resolution and navigation reset. Document this behaviour in the SupabaseSessionManager API contract.

Contingency: If claims drift is detected in production and causes data visibility issues, provide a force-refresh mechanism in the UI (pull-to-refresh on home screen) that clears cached role state and re-fetches from Supabase, accompanied by a user-visible toast indicating the session was refreshed.

medium impact medium prob security

Allowing session resumption from cached local token when offline introduces a window where a revoked or invalidated session can still grant app access. For example, if a coordinator deactivates a peer mentor's account while the mentor is offline, the mentor continues to have access until connectivity is restored and the token is validated server-side.

Mitigation & Contingency

Mitigation: Set a maximum offline grace period (e.g., 24 hours) stored alongside the token in SecureSessionStorage. If the grace period is exceeded, force a full credential re-login regardless of connectivity status. Scope offline access to read-only operations only, requiring connectivity for any write that reaches Supabase.

Contingency: If the offline grace period logic is found to be insufficient for compliance, implement remote session invalidation via a lightweight push notification that clears SecureSessionStorage even when the app is backgrounded, using FCM with a data-only message.