critical priority medium complexity backend pending backend specialist Tier 3

Acceptance Criteria

getSession() returns Future<SessionResult?> — null when no valid session exists, SessionResult when one does
restoreSession() attempts to resume a Supabase session from secure storage and returns bool (restored: true/false)
On successful signIn(), the session tokens (access token + refresh token) are persisted to flutter_secure_storage under namespaced keys
On app launch (before showing any screen), restoreSession() is called; if successful, the user is navigated directly to their role-appropriate home screen
If the stored session is expired, Supabase's recoverSession / refreshSession is attempted with the refresh token before returning null
On signOut(), all persisted session tokens are deleted from secure storage
Session restoration logic is encapsulated in AuthRepository, not in SupabaseAuthService directly
Riverpod provider exposes a sessionStateProvider (StreamProvider or StateNotifierProvider) that emits auth state changes
If secure storage read fails (e.g., keychain locked), return null gracefully — never crash the app
flutter analyze passes with no warnings

Technical Requirements

frameworks
Flutter
Dart
Supabase Flutter SDK
Riverpod
flutter_secure_storage
apis
Supabase Auth — recoverSession
Supabase Auth — onAuthStateChange stream
flutter_secure_storage read/write/delete
data models
SessionResult
AuthRepository
performance requirements
restoreSession() must complete within 2 seconds — it runs on the splash screen critical path
Secure storage reads are async I/O; never block the UI thread
security requirements
Access tokens and refresh tokens stored using flutter_secure_storage with iCloudSync: false
Keys must be namespaced (e.g., 'auth.access_token', 'auth.refresh_token') to avoid collisions
Never store tokens in shared preferences, hive, or any unencrypted storage
On iOS, use kSecAttrAccessibleWhenUnlockedThisDeviceOnly to prevent iCloud backup of tokens

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Supabase Flutter SDK v2+ has built-in session persistence via supabase.auth.onAuthStateChange stream — check if this already handles token storage before reimplementing. If the SDK persists tokens internally, AuthRepository only needs to subscribe to onAuthStateChange and expose the current session as a Riverpod stream. Avoid duplicating storage logic if Supabase handles it. For the restoreSession path: use a GoRouter redirect or a top-level Consumer that listens to sessionStateProvider.

On iOS, test with a device lock/unlock cycle to confirm keychain accessibility settings work correctly. Add a //TODO comment if Supabase SDK storage behavior changes in a future version.

Testing Requirements

Unit tests with mocked FlutterSecureStorage and MockSupabaseClient: (1) Verify signIn success triggers a write to secure storage with correct keys. (2) Verify restoreSession() reads tokens and calls supabase.auth.recoverSession(). (3) Verify restoreSession() returns false when storage is empty. (4) Verify restoreSession() returns false when recoverSession throws (expired token).

(5) Verify signOut() deletes all auth keys from secure storage. Widget test: confirm app shows home screen (not login) when restoreSession() returns true. Use flutter_test, mocktail.

Component
Authentication Service
service medium
Epic Risks (2)
high impact medium prob integration

Supabase GoTrue returns HTTP error codes and string messages that may change between SDK versions. Incorrect or incomplete mapping could cause the wrong user-facing message to be shown (e.g., showing a generic error instead of a specific credential error), violating the plain-language feedback acceptance criteria and potentially exposing security-sensitive information.

Mitigation & Contingency

Mitigation: Pin the supabase_flutter SDK to a specific minor version in pubspec.yaml. Write integration tests that mock the Supabase HTTP layer and assert each error code maps to the correct domain exception. Document the mapping table as a constant in AuthService.

Contingency: If an unrecognized error code is received at runtime, catch it as an UnknownAuthException and display a generic safe message. Alert via crash reporting for triage and SDK update.

medium impact medium prob technical

If the user taps the sign-in button multiple times rapidly, concurrent authentication requests could result in race conditions: duplicate network calls, out-of-order state emissions, or multiple session tokens being written to secure storage.

Mitigation & Contingency

Mitigation: Use bloc concurrency transformer (droppable or restartable) to ensure only one authentication event is processed at a time. The BLoC should guard against submission while in LoginLoading state.

Contingency: Add a UI-level disable on the submit button when loading state is active as a secondary guard independent of BLoC concurrency control.