critical priority low complexity backend pending backend specialist Tier 3

Acceptance Criteria

signInWithEmailAndPassword(email, password) returns AuthResult.success(session) on valid credentials
signInWithEmailAndPassword returns AuthResult.failure(AuthError.invalidCredentials) when Supabase returns a 400 invalid_grant or invalid_login_credentials error
signInWithEmailAndPassword returns AuthResult.failure(AuthError.networkError) when the device is offline or a SocketException is thrown
signInWithEmailAndPassword returns AuthResult.failure(AuthError.tooManyRequests) when Supabase returns a 429 rate-limit response
signInWithEmailAndPassword returns AuthResult.failure(AuthError.unknown(message)) for all other Supabase error codes
On successful sign-in, the session is persisted to encrypted storage before AuthResult.success is returned
signOut() calls Supabase signOut(), deletes the cached token from encrypted storage, and returns void
signOut() does not throw if the user is already signed out or if the network call fails — it always clears local state
AuthResult is a sealed class; all callers are forced to handle both success and failure variants
AuthError is an exhaustive enum or sealed class covering at minimum: invalidCredentials, networkError, tooManyRequests, emailNotConfirmed, unknown

Technical Requirements

frameworks
Flutter
Supabase Flutter SDK
flutter_secure_storage
apis
Supabase GoTrue signInWithPassword()
Supabase GoTrue signOut()
data models
AuthResult (sealed: success | failure)
AuthError (sealed/enum)
Session
UserCredentials (email, password — passed as value object, never stored)
performance requirements
signIn must surface an error to the caller within 5 seconds if the network is unreachable (do not wait for default socket timeout)
signOut local state clearing must be synchronous even if the Supabase network call is still in flight
security requirements
Passwords must never be logged, stored in variables beyond the immediate call scope, or included in error messages
Supabase error responses must be sanitised before being surfaced as AuthError.unknown to prevent leaking internal server details
signOut must delete all tokens from encrypted storage regardless of whether the Supabase API call succeeds

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Define a private _mapSupabaseError(AuthException e) helper that switches on e.statusCode and e.message to return the correct AuthError. This keeps the sign-in method readable. For signOut, use a try/finally pattern: always execute storage.deleteToken() in the finally block so local state is cleared regardless of network outcome. Use Supabase's AuthException type specifically rather than catching dynamic/Object.

Coordinate with task-007 so the same encrypted storage adapter instance is injected via constructor — do not create a second instance. Consider adding a currentUser getter that reads from the in-memory Supabase session rather than re-reading storage on every call.

Testing Requirements

Unit tests must cover: successful sign-in maps to AuthResult.success and writes to storage; each mapped Supabase error code produces the correct AuthError variant; signOut deletes the stored token even when the Supabase call throws. Use mockito or mocktail to mock SupabaseClient and the encrypted storage adapter. Do not make real network calls. Verify that the password value is not accessible after the signIn call completes (scope check).

Target 100% branch coverage on both methods.

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).