# Authentication Troubleshooting Guide **Date**: October 10, 2025 This guide helps debug authentication issues in the Retail POS app. **For implementation details, see:** [AUTH_IMPLEMENTATION_SUMMARY.md](AUTH_IMPLEMENTATION_SUMMARY.md) **For quick start, see:** [AUTH_READY.md](AUTH_READY.md) --- ## Common Issues ### Issue 1: 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 Cause:** State not updating properly or UI not watching state **Solution:** 1. Verify `AuthWrapper` uses `ref.watch(authProvider)` not `ref.read()` 2. Check auth provider has `@Riverpod(keepAlive: true)` annotation 3. Verify login method explicitly sets `isAuthenticated: true` in state 4. Check logs for successful state update --- ### Issue 2: Auto-Login Not Working **Symptoms:** - Login with Remember Me checked - Close and reopen app - Shows login page instead of auto-login **Common Causes:** **A. Remember Me Not Enabled** - Check the Remember Me checkbox was actually checked during login - Look for log: `Token saved to secure storage (persistent)` - If you see `Token NOT saved (session only)`, checkbox was not checked **B. Token Not Being Loaded on Startup** - Check logs for: `Initializing auth state...` - If missing, `initialize()` is not being called in `app.dart` - Verify `app.dart` has `initState()` that calls `auth.initialize()` **C. Profile API Failing** - Token loads but profile fetch fails - Check logs for: `Failed to get profile: [error]` - Common causes: Token expired, backend not running, network error - Solution: Ensure backend is running and token is valid **D. UserModel Parsing Error** - Error: `type 'Null' is not a subtype of type 'String' in type cast` - Cause: Backend `/auth/profile` response missing `createdAt` field - Solution: Already fixed - UserModel now handles optional `createdAt` --- ### Issue 3: Token Not Added to API Requests **Symptoms:** - Login successful - But subsequent API calls return 401 Unauthorized - API requests missing `Authorization` header **Solution:** 1. Verify `DioClient.setAuthToken()` is called after login 2. Check `DioClient` has interceptor that adds `Authorization` header 3. Look for log: `Token set in DioClient` 4. Verify dio interceptor: `options.headers['Authorization'] = 'Bearer $_authToken'` --- ### Issue 4: "Connection Refused" Error **Symptoms:** - Login fails immediately - Error: Connection refused or network error **Solution:** - Ensure backend is running at `http://localhost:3000` - Check API endpoint URL in `lib/core/constants/api_constants.dart` - Verify backend CORS is configured (if running on web) - Test backend directly: `curl http://localhost:3000/api/auth/login` --- ### Issue 5: Invalid Credentials Error Even with Correct Password **Symptoms:** - Entering correct credentials - Always getting "Invalid email or password" **Solution:** - Verify user exists in backend database - Check backend logs for authentication errors - Test login directly with curl or Postman - Verify email and password match backend user --- ## 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 Tools ### Enable Debug Logging The auth system has extensive logging. Look for these key logs: **Login Flow:** ``` 🔐 Repository: Starting login (rememberMe: true/false)... 💾 SecureStorage: Token saved successfully ✅ Login SUCCESS: user=Name, token length=XXX ``` **Auto-Login Flow:** ``` 🚀 Initializing auth state... 🔍 Has token in storage: true/false 🚀 Token found, fetching user profile... ✅ Profile loaded: Name ``` **Common Error Logs:** ``` ❌ No token found in storage ❌ Failed to get profile: [error message] ❌ Login failed: [error message] ``` ### Debug Checklist If auth flow still not working: 1. **Check Provider State:** ```dart final authState = ref.read(authProvider); print('isAuthenticated: ${authState.isAuthenticated}'); print('user: ${authState.user?.name}'); print('errorMessage: ${authState.errorMessage}'); ``` 2. **Check Token Storage:** ```dart final storage = SecureStorage(); final hasToken = await storage.hasAccessToken(); print('Has token: $hasToken'); ``` 3. **Check Backend:** ```bash curl -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"test@retailpos.com","password":"Test123!"}' ``` 4. **Check Logs:** - Watch for errors in Flutter console - Check backend logs for API errors - Look for network errors or timeouts --- ## 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.