Phase 01: Authentication & Account Basics - Standard stack identified (supabase_flutter v2.12.0) - Architecture patterns documented (Flutter + Supabase) - Pitfalls catalogued (session persistence, auth state, password reset) - Code examples provided from official sources
13 KiB
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:
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:
// 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:
// 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:
// 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
// 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
// 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
// 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
// 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
// Source: https://pub.dev/packages/supabase_flutter
class MySecureStorage extends LocalStorage {
final storage = FlutterSecureStorage();
@override
Future<void> initialize() async {}
@override
Future<String?> accessToken() async {
return storage.read(key: supabasePersistSessionKey);
}
@override
Future<void> persistSession(String persistSessionString) async {
return storage.write(key: supabasePersistSessionKey, value: persistSessionString);
}
@override
Future<void> 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
-
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
-
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
-
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)