# Phase 1: Authentication & Account Basics - Research **Researched:** 2026-01-28 **Domain:** Flutter + Supabase Authentication **Confidence:** HIGH ## Summary Sage requires Flutter-based authentication using Supabase as the backend service. The established pattern is to use the `supabase_flutter` package (v2.12.0) which provides comprehensive authentication capabilities including email/password signup, password reset, session persistence, and auth state management. The package handles session persistence using SharedPreferences by default, with optional secure storage support via FlutterSecureStorage for enhanced security. **Primary recommendation:** Use supabase_flutter v2.12.0 with default SharedPreferences storage and PKCE flow for secure authentication across Android and web platforms. ## Standard Stack The established libraries/tools for this domain: ### Core | Library | Version | Purpose | Why Standard | |---------|---------|---------|--------------| | supabase_flutter | ^2.12.0 | Official Supabase client for Flutter | Provides complete auth implementation, session management, and real-time auth state updates | | flutter_secure_storage | ^9.0.0 | Enhanced session security (optional) | Industry standard for storing sensitive data securely on mobile devices | | shared_preferences | ^2.0.0 | Default session persistence | Built-in Supabase storage method, lightweight and reliable | ### Supporting | Library | Version | Purpose | When to Use | |---------|---------|---------|-------------| | go_router | ^13.0.0 | Navigation management | For handling auth-based routing and redirects | | riverpod | ^2.5.0 | State management | For managing global auth state across the app | ### Alternatives Considered | Instead of | Could Use | Tradeoff | |------------|-----------|----------| | supabase_flutter | firebase_auth | Firebase would work but Supabase is already decided as project standard, better PostgreSQL integration | | supabase_flutter | custom auth implementation | Custom auth is significantly more complex, security risks, requires implementing session management, password hashing, token refresh manually | **Installation:** ```bash flutter pub add supabase_flutter flutter_secure_storage ``` ## Architecture Patterns ### Recommended Project Structure ``` src/ ├── lib/ │ ├── main.dart # Supabase initialization │ ├── core/ │ │ ├── constants/ │ │ │ └── supabase_constants.dart # Supabase URL and keys │ │ └── errors/ │ │ └── auth_exceptions.dart # Custom auth error handling │ ├── features/ │ │ └── authentication/ │ │ ├── data/ │ │ │ └── models/ │ │ │ └── auth_user.dart # User model │ │ ├── domain/ │ │ │ └── repositories/ │ │ │ └── auth_repository.dart # Auth interface │ │ └── presentation/ │ │ ├── pages/ │ │ │ ├── login_page.dart │ │ │ ├── signup_page.dart │ │ │ └── reset_password_page.dart │ │ └── widgets/ │ │ ├── auth_form.dart │ │ └── auth_button.dart │ └── providers/ │ └── auth_provider.dart # Global auth state management ``` ### Pattern 1: Supabase Initialization **What:** Initialize Supabase at app startup with proper configuration **When to use:** In main.dart before runApp() **Example:** ```dart // Source: https://pub.dev/packages/supabase_flutter void main() async { WidgetsFlutterBinding.ensureInitialized(); await Supabase.initialize( url: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY, authOptions: FlutterAuthClientOptions( localStorage: const SharedPreferencesLocalStorage(), // Default, secure enough // For enhanced security on mobile: // localStorage: MySecureStorage(), ), ); runApp(MyApp()); } final supabase = Supabase.instance.client; ``` ### Pattern 2: Auth State Management **What:** Use StreamBuilder or Riverpod to listen to auth state changes **When to use:** Throughout the app for UI updates based on auth state **Example:** ```dart // Source: https://supabase.com/docs/reference/dart/auth-onauthstatechange class AuthProvider extends ChangeNotifier { User? _user; User? get user => _user; AuthProvider() { supabase.auth.onAuthStateChange.listen((data) { final AuthChangeEvent event = data.event; final Session? session = data.session; _user = session?.user; notifyListeners(); }); } } ``` ### Pattern 3: Authentication Methods **What:** Use Supabase auth methods for signup, login, password reset **When to use:** For all auth operations **Example:** ```dart // Email signup final AuthResponse res = await supabase.auth.signUp( email: email, password: password, ); // Email login final AuthResponse res = await supabase.auth.signInWithPassword( email: email, password: password, ); // Password reset await supabase.auth.resetPasswordForEmail( email, redirectTo: 'http://example.com/account/update-password', ); ``` ### Anti-Patterns to Avoid - **Manual token management:** Don't store JWT tokens manually or implement custom refresh logic - **Ignoring auth state:** Don't skip onAuthStateChange listener - leads to UI inconsistencies - **Hardcoded credentials:** Never store Supabase keys directly in code - use environment variables ## Don't Hand-Roll Problems that look simple but have existing solutions: | Problem | Don't Build | Use Instead | Why | |---------|-------------|-------------|-----| | Session persistence | Custom token storage with SharedPreferences | Supabase built-in session management | Handles token expiration, refresh, security automatically | | Password reset email flow | Custom SMTP + email templates | Supabase resetPasswordForEmail() | Built-in rate limiting, secure token generation, proper email handling | | Auth state management | Manual user state with setState() | onAuthStateChange stream | Automatic updates across all app components | | Error handling | Try-catch for every auth call | AuthException handling | Consistent error messages, proper user feedback | **Key insight:** Supabase handles all security-critical aspects like token refresh, session validation, and password reset flow securely. Custom implementations risk security vulnerabilities. ## Common Pitfalls ### Pitfall 1: Session Not Persisting **What goes wrong:** User gets logged out when app restarts **Why it happens:** Missing localStorage configuration or using EmptyLocalStorage **How to avoid:** Ensure proper initialization with default localStorage **Warning signs:** User must log in every time app launches ### Pitfall 2: Auth State Not Updating UI **What goes wrong:** UI doesn't reflect login/logout status **Why it happens:** Not listening to onAuthStateChange events **How to avoid:** Always subscribe to auth state changes in app initialization **Warning signs:** User stuck on login screen after successful authentication ### Pitfall 3: Password Reset Flow Fails **What goes wrong:** Reset links don't work or redirect incorrectly **Why it happens:** Missing redirect URL configuration in Supabase dashboard **How to avoid:** Configure redirect URLs in Authentication > URL Configuration **Warning signs:** Users report broken password reset emails ### Pitfall 4: Web Service Worker Issues **What goes wrong:** Flutter web app fails to register service worker **Why it happens:** Improper web hosting configuration for password reset callback **How to avoid:** Ensure web server serves correct MIME types for service workers **Warning signs:** Console errors about service worker MIME types ### Pitfall 5: Environment Variable Exposure **What goes wrong:** Supabase keys exposed in client code **Why it happens:** Hardcoding values instead of using environment variables **How to avoid:** Use flutter_dotenv for local dev, proper CI/CD secrets for production **Warning signs:** Keys visible in compiled code or version control ## Code Examples Verified patterns from official sources: ### Email Sign Up ```dart // Source: https://supabase.com/docs/reference/dart/auth-signup final AuthResponse res = await supabase.auth.signUp( email: 'example@email.com', password: 'example-password', ); final Session? session = res.session; final User? user = res.user; ``` ### Email Sign In ```dart // Source: https://supabase.com/docs/reference/dart/auth-signinwithpassword final AuthResponse res = await supabase.auth.signInWithPassword( email: 'example@email.com', password: 'example-password', ); final Session? session = res.session; final User? user = res.user; ``` ### Password Reset ```dart // Source: https://supabase.com/docs/guides/auth/passwords await supabase.auth.resetPasswordForEmail( 'valid.email@supabase.io', redirectTo: 'http://example.com/account/update-password', ); ``` ### Auth State Listening ```dart // Source: https://supabase.com/docs/reference/dart/auth-onauthstatechange final authSubscription = supabase.auth.onAuthStateChange.listen((data) { final AuthChangeEvent event = data.event; final Session? session = data.session; switch (event) { case AuthChangeEvent.signedIn: // Navigate to home break; case AuthChangeEvent.signedOut: // Navigate to login break; } }); ``` ### Secure Storage Option ```dart // Source: https://pub.dev/packages/supabase_flutter class MySecureStorage extends LocalStorage { final storage = FlutterSecureStorage(); @override Future initialize() async {} @override Future accessToken() async { return storage.read(key: supabasePersistSessionKey); } @override Future persistSession(String persistSessionString) async { return storage.write(key: supabasePersistSessionKey, value: persistSessionString); } @override Future removePersistedSession() async { return storage.delete(key: supabasePersistSessionKey); } } ``` ## State of the Art | Old Approach | Current Approach | When Changed | Impact | |--------------|-----------------|--------------|--------| | Hive-based storage (v1) | SharedPreferences-based storage (v2) | v2.0 release | Simpler, faster app startup, better platform support | | Implicit auth flow | PKCE flow (default in v2) | v2.0 release | More secure for deep links and OAuth | | Manual token refresh | Automatic token refresh | v1.x | Session persistence handled automatically | | SupabaseAuth class | Direct client access | v2.0 release | Cleaner API, no wrapper classes needed | **Deprecated/outdated:** - **SupabaseAuth.initialSession**: Use supabase.auth.currentSession instead - **Hive storage**: Replaced with SharedPreferences in v2 - **signInWithApple() method**: Removed from core package, use separate sign_in_with_apple package - **Provider enum**: Renamed to OAuthProvider to avoid conflicts ## Open Questions 1. **Enhanced Security Requirements** - What we know: FlutterSecureStorage available for enhanced session security - What's unclear: Does Sage require enhanced security for food inventory app sensitivity? - Recommendation: Use SharedPreferences for v1, evaluate FlutterSecureStorage for production 2. **Email Template Customization** - What we know: Supabase provides default email templates - What's unclear: Does Sage need custom branding for password reset emails? - Recommendation: Use default templates for v1, customize after user feedback 3. **Rate Limiting Strategy** - What we know: Supabase has built-in rate limiting (2 emails/hour default) - What's unclear: Are these limits sufficient for household app usage patterns? - Recommendation: Monitor and configure custom SMTP if limits are reached ## Sources ### Primary (HIGH confidence) - supabase_flutter v2.12.0 documentation - Authentication methods, session management, initialization patterns - Supabase official docs - Password reset flow, email authentication best practices ### Secondary (MEDIUM confidence) - Flutter Secure Storage package documentation - Enhanced session persistence options - Medium articles on Flutter auth patterns - Real-world implementation patterns (verified with official docs) ### Tertiary (LOW confidence) - Stack Overflow discussions on specific error scenarios - Limited verification, use with caution ## Metadata **Confidence breakdown:** - Standard stack: HIGH - Based on official Supabase Flutter package documentation - Architecture: HIGH - Verified with multiple official sources and community patterns - Pitfalls: HIGH - Official documentation clearly documents common issues and solutions **Research date:** 2026-01-28 **Valid until:** 2026-02-27 (30 days for stable authentication libraries)