critical priority low complexity backend pending backend specialist Tier 2

Acceptance Criteria

restoreSession() returns SessionRestoreResult.restored with a valid Session object when a non-expired token exists in encrypted storage
restoreSession() returns SessionRestoreResult.refreshed with the new Session when the stored access token is expired but the refresh token is still valid
restoreSession() returns SessionRestoreResult.noSession when encrypted storage contains no token
restoreSession() returns SessionRestoreResult.expired when both access and refresh tokens are invalid or revoked by Supabase
The method calls Supabase recoverSession() to validate the cached token before returning a restored result
A successful refresh writes the new session back to encrypted storage via the storage adapter
No network call is made when storage returns an empty token (fast path)
All four SessionRestoreResult variants are sealed/exhaustive so callers are forced to handle every case at compile time
The method completes within 2 seconds on a standard mobile network connection
Session restoration does not throw uncaught exceptions; all Supabase errors are caught and mapped to SessionRestoreResult.expired or SessionRestoreResult.noSession as appropriate

Technical Requirements

frameworks
Flutter
Supabase Flutter SDK
flutter_secure_storage
apis
Supabase GoTrue recoverSession()
Supabase GoTrue refreshSession()
data models
Session
SessionRestoreResult (sealed class: restored | refreshed | expired | noSession)
AuthToken (access_token, refresh_token, expires_at)
performance requirements
Fast path (no stored token) must complete synchronously without any network I/O
Full restore path (valid token) must complete within 2 seconds on 4G
Refresh path must complete within 3 seconds on 4G
security requirements
Tokens are only ever read from and written to the encrypted storage adapter established in task-006
Expired tokens must be deleted from storage before returning SessionRestoreResult.expired
No token values are logged or exposed in stack traces
Supabase session metadata (user ID, role) must not be stored in plaintext SharedPreferences

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Define SessionRestoreResult as a Dart sealed class with four variants so the compiler enforces exhaustive handling in switch expressions. The method signature should be: Future restoreSession(). Read the raw token string from the encrypted storage adapter; if null or empty, return noSession immediately. Pass the token to Supabase recoverSession(); if it throws AuthException with 'token expired' or similar, attempt refreshSession() with the stored refresh token.

On refresh success, persist the new session via the storage adapter. On any other error, delete the stored token and return expired. Avoid catching broad Exception types — catch AuthException specifically so unexpected errors propagate. Keep this method free of UI concerns; the BLoC/Cubit layer is responsible for routing.

Testing Requirements

Unit tests must cover all four SessionRestoreResult branches using mocked SupabaseClient and mocked encrypted storage adapter. Verify that expired-token path calls both recoverSession() (fails) and that the result is .expired. Verify that the refresh path calls refreshSession() and writes the new token to storage. Verify the noSession fast path makes zero network calls.

All tests must run offline and complete in under 300ms total. Achieve 100% branch coverage on the restoreSession() method.

Component
Authentication Repository
data low
Epic Risks (2)
high impact low prob integration

Supabase client initialization may fail silently in certain Flutter environments if environment variables are missing or the anon key is rotated, leading to runtime null-pointer errors throughout the auth layer.

Mitigation & Contingency

Mitigation: Add explicit assertion checks during app startup that verify the Supabase client is initialized before the router resolves. Document required --dart-define keys in the project README and add a CI step that validates their presence.

Contingency: Implement a fallback initialization error screen with a clear message and a retry button. Log initialization failures to crash reporting immediately.

medium impact medium prob technical

The flutter_secure_storage package behaviour differs between iOS Keychain and Android Keystore implementations. On Android, biometric-enrolled devices may require additional authentication to read stored tokens, causing unexpected session read failures.

Mitigation & Contingency

Mitigation: Test the repository on Android devices with and without biometric enrollment early in development. Use accessibility options in flutter_secure_storage to configure whether biometric authentication is required for storage access.

Contingency: If biometric-gated storage causes regressions, fall back to a non-biometric storage option for session tokens (reserving biometric-gated storage for higher-sensitivity credentials only).