# Authentication Troubleshooting Guide **Date**: October 10, 2025 --- ## Issue: Login Successful But No Navigation ### Symptoms - Login API call succeeds - Token is saved - But app doesn't navigate to MainScreen - AuthWrapper doesn't react to state change ### Root Causes Fixed #### 1. **GetIt Dependency Injection Error** ✅ FIXED - **Problem**: AuthRepository was trying to use GetIt but wasn't registered - **Solution**: Migrated to pure Riverpod dependency injection - **Files Changed**: `lib/features/auth/presentation/providers/auth_provider.dart` #### 2. **Circular Dependency in Auth Provider** ✅ FIXED - **Problem**: `Auth.build()` was calling async `_checkAuthStatus()` causing circular dependency - **Solution**: Moved initialization to separate `initialize()` method - **Files Changed**: `lib/features/auth/presentation/providers/auth_provider.dart`, `lib/app.dart` #### 3. **Provider Not Kept Alive** ✅ FIXED - **Problem**: Auth state provider was being disposed between rebuilds - **Solution**: Added `@Riverpod(keepAlive: true)` to Auth provider - **Files Changed**: `lib/features/auth/presentation/providers/auth_provider.dart` #### 4. **State Not Updating Properly** ✅ FIXED - **Problem**: `copyWith` method wasn't properly setting `isAuthenticated: true` - **Solution**: Updated login/register methods to create new `AuthState` with explicit values - **Files Changed**: `lib/features/auth/presentation/providers/auth_provider.dart` --- ## How Auth Flow Should Work ### 1. App Startup ``` main() → ProviderScope created → RetailApp builds → initState() schedules auth initialization → auth.initialize() checks for saved token → If token found: loads user profile, sets isAuthenticated = true → If no token: sets isAuthenticated = false ``` ### 2. Login Flow ``` User enters credentials → Taps Login button → _handleLogin() called → ref.read(authProvider.notifier).login(email, password) → API call to /api/auth/login → Success: saves token, sets user, sets isAuthenticated = true → AuthWrapper watches authProvider → isAuthenticated changes to true → AuthWrapper rebuilds → Shows MainScreen instead of LoginPage ``` ### 3. Logout Flow ``` User taps Logout in Settings → Confirmation dialog shown → ref.read(authProvider.notifier).logout() → Token cleared from secure storage → DioClient token cleared → State set to isAuthenticated = false → AuthWrapper rebuilds → Shows LoginPage ``` --- ## Debug Checklist If auth flow still not working, check these: ### 1. Verify Provider State ```dart // Add this to login_page.dart _handleLogin after login success final authState = ref.read(authProvider); print('🔐 Auth State after login:'); print(' isAuthenticated: ${authState.isAuthenticated}'); print(' user: ${authState.user?.name}'); print(' isLoading: ${authState.isLoading}'); print(' errorMessage: ${authState.errorMessage}'); ``` ### 2. Verify AuthWrapper Reaction ```dart // Add this to auth_wrapper.dart build method @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); print('🔄 AuthWrapper rebuild:'); print(' isAuthenticated: ${authState.isAuthenticated}'); print(' isLoading: ${authState.isLoading}'); print(' user: ${authState.user?.name}'); // ... rest of build method } ``` ### 3. Verify Token Saved ```dart // Add this to auth_repository_impl.dart login method after saving token print('💾 Token saved: ${authResponse.accessToken.substring(0, 20)}...'); print('💾 DioClient token set'); ``` ### 4. Verify API Response ```dart // Add this to auth_remote_datasource.dart login method print('📡 Login API response:'); print(' Status: ${response.statusCode}'); print(' User: ${response.data['user']?['name']}'); print(' Token length: ${response.data['accessToken']?.length}'); ``` --- ## Common Issues and Solutions ### Issue: State Updates But UI Doesn't Rebuild **Cause**: Using `ref.read()` instead of `ref.watch()` in AuthWrapper **Solution**: Ensure AuthWrapper uses `ref.watch(authProvider)` ```dart final authState = ref.watch(authProvider); // ✅ Correct - watches for changes // NOT ref.read(authProvider) // ❌ Wrong - doesn't rebuild ``` ### Issue: Login Success But isAuthenticated = false **Cause**: State update not explicitly setting `isAuthenticated: true` **Solution**: Create new AuthState with explicit values ```dart state = AuthState( user: authResponse.user, isAuthenticated: true, // ✅ Explicit value isLoading: false, errorMessage: null, ); ``` ### Issue: Provider Disposes Between Rebuilds **Cause**: Provider not marked as `keepAlive` **Solution**: Add `@Riverpod(keepAlive: true)` to Auth provider ```dart @Riverpod(keepAlive: true) // ✅ Keeps state alive class Auth extends _$Auth { // ... } ``` ### Issue: Circular Dependency Error **Cause**: Calling async operations in `build()` method **Solution**: Use separate initialization method ```dart @override AuthState build() { return const AuthState(); // ✅ Sync only } Future initialize() async { // ✅ Async operations here } ``` --- ## Testing the Fix ### Test 1: Fresh App Start (No Token) 1. **Clear app data** or use fresh install 2. **Run app**: `flutter run` 3. **Expected**: Shows LoginPage immediately 4. **Result**: ✅ Pass / ❌ Fail ### Test 2: Login Flow 1. **Start at LoginPage** 2. **Enter credentials**: admin@retailpos.com / Admin123! 3. **Tap Login** 4. **Expected**: - Loading indicator appears - On success: Navigate to MainScreen with bottom tabs 5. **Result**: ✅ Pass / ❌ Fail ### Test 3: Token Persistence 1. **Login successfully** 2. **Close app completely** 3. **Restart app** 4. **Expected**: - Shows loading briefly - Automatically goes to MainScreen (no login needed) 5. **Result**: ✅ Pass / ❌ Fail ### Test 4: Logout Flow 1. **While logged in, go to Settings tab** 2. **Tap Logout button** 3. **Confirm logout** 4. **Expected**: Navigate back to LoginPage 5. **Result**: ✅ Pass / ❌ Fail ### Test 5: Invalid Credentials 1. **Enter wrong email/password** 2. **Tap Login** 3. **Expected**: - Shows error SnackBar - Stays on LoginPage - Error message displayed 5. **Result**: ✅ Pass / ❌ Fail --- ## Architecture Diagram ``` ┌─────────────────────────────────────────────────┐ │ ProviderScope │ │ ┌───────────────────────────────────────────┐ │ │ │ RetailApp │ │ │ │ (initializes auth on startup) │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ │ │ MaterialApp │ │ │ │ │ │ ┌───────────────────────────────┐ │ │ │ │ │ │ │ AuthWrapper │ │ │ │ │ │ │ │ (watches authProvider) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ if isAuthenticated: │ │ │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ │ │ MainScreen │ │ │ │ │ │ │ │ │ │ (with bottom tabs) │ │ │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ else: │ │ │ │ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ │ │ LoginPage │ │ │ │ │ │ │ │ │ │ (login form) │ │ │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ │ └───────────────────────────────┘ │ │ │ │ │ └─────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ ↕ ┌───────────────┐ │ authProvider │ │ (keepAlive) │ └───────────────┘ ↕ ┌───────────────────────┐ │ authRepository │ │ ↓ │ │ authRemoteDataSource │ │ ↓ │ │ dioClient │ │ ↓ │ │ secureStorage │ └───────────────────────┘ ``` --- ## Files Modified ### Core Auth Files - ✅ `lib/features/auth/presentation/providers/auth_provider.dart` - Added `@Riverpod(keepAlive: true)` to Auth provider - Fixed `copyWith` method with `clearUser` and `clearError` flags - Updated login/register to explicitly set `isAuthenticated: true` - Moved auth check to `initialize()` method - ✅ `lib/app.dart` - Changed from `ConsumerWidget` to `ConsumerStatefulWidget` - Added `initState()` to call `auth.initialize()` - ✅ `lib/main.dart` - Removed GetIt initialization - Using pure Riverpod for DI - ✅ `lib/features/auth/presentation/widgets/auth_wrapper.dart` - Already correct - uses `ref.watch(authProvider)` - ✅ `lib/features/auth/presentation/pages/login_page.dart` - Already correct - login logic properly calls provider --- ## Expected Behavior After Fixes 1. ✅ App starts → auth initializes → shows LoginPage (if no token) 2. ✅ Login success → state updates → AuthWrapper rebuilds → shows MainScreen 3. ✅ Token persists → app restart → auto-login works 4. ✅ Logout → state clears → AuthWrapper rebuilds → shows LoginPage 5. ✅ All tabs accessible after login (Home, Products, Categories, Settings) --- ## Next Steps If Still Not Working 1. **Add Debug Logs**: Add print statements to trace state changes 2. **Check Backend**: Ensure API endpoints are working and returning correct data 3. **Verify Token Format**: Check that JWT token is valid format 4. **Check API Response Structure**: Ensure response matches model expectations 5. **Test with Hot Restart**: Try `r` (hot reload) vs `R` (hot restart) in Flutter --- **Status**: All known issues fixed. Auth flow should work correctly now. If issues persist, add debug logging as described above to trace the exact point of failure.