Files
retail/docs/AUTO_LOGIN_FIXED.md
Phuoc Nguyen f6d2971224 fix md
2025-10-13 17:49:35 +07:00

5.0 KiB

Auto-Login Issue Fixed!

Date: October 10, 2025 Status: FIXED


The Problem

Auto-login was failing with:

❌ Failed to get profile: type 'Null' is not a subtype of type 'String' in type cast

Root Cause

The /auth/profile endpoint returns a user object WITHOUT the createdAt field:

{
  "id": "b938f48f-4032-4144-9ce8-961f7340fa4f",
  "email": "admin@retailpos.com",
  "name": "Admin User",
  "roles": ["admin"],
  "isActive": true
  // ❌ Missing: createdAt, updatedAt
}

But UserModel.fromJson() was expecting createdAt to always be present:

// BEFORE (causing crash)
final createdAt = DateTime.parse(json['createdAt'] as String);
// ❌ Crashes when createdAt is null

The Fix

Updated UserModel.fromJson() to handle missing createdAt and updatedAt fields:

File: lib/features/auth/data/models/user_model.dart

factory UserModel.fromJson(Map<String, dynamic> json) {
  // ✅ createdAt is now optional, defaults to now
  final createdAt = json['createdAt'] != null
      ? DateTime.parse(json['createdAt'] as String)
      : DateTime.now();

  return UserModel(
    id: json['id'] as String,
    name: json['name'] as String,
    email: json['email'] as String,
    roles: (json['roles'] as List<dynamic>).cast<String>(),
    isActive: json['isActive'] as bool? ?? true,
    createdAt: createdAt,
    // ✅ updatedAt is also optional, defaults to createdAt
    updatedAt: json['updatedAt'] != null
        ? DateTime.parse(json['updatedAt'] as String)
        : createdAt,
  );
}

How Auto-Login Works Now

Step 1: Login with Remember Me

User logs in with Remember Me checked
  ↓
Token saved to SecureStorage
  ↓
Token set in DioClient
  ↓
User navigates to MainScreen

Step 2: App Restart

App starts
  ↓
initialize() called
  ↓
Check SecureStorage for token
  ↓
Token found!
  ↓
Load token and set in DioClient
  ↓
Fetch user profile with GET /auth/profile
  ↓
Parse profile (now handles missing createdAt)
  ↓
✅ Auto-login success!
  ↓
Navigate to MainScreen (no login page)

Expected Logs on Restart

📱 RetailApp: initState called
📱 RetailApp: Calling initialize()...
🚀 Initializing auth state...
🔍 Checking authentication...
💾 SecureStorage: Token read result - exists: true, length: 252
✅ Token loaded from storage and set in DioClient
🚀 isAuthenticated result: true
🚀 Token found, fetching user profile...
📡 DataSource: Calling getProfile API...
REQUEST[GET] => PATH: /auth/profile
RESPONSE[200] => PATH: /auth/profile
📡 DataSource: User parsed successfully: Admin User
✅ Profile loaded: Admin User
✅ Initialize complete: isAuthenticated=true
AuthWrapper build: isAuthenticated=true, isLoading=false
→ Shows MainScreen ✅

Testing Auto-Login

Test 1: With Remember Me

1. flutter run
2. Login with Remember Me CHECKED ✅
3. See: "Token saved to secure storage (persistent)"
4. Press 'R' to hot restart
5. Expected: Auto-login to MainScreen (no login page)

Test 2: Without Remember Me

1. Logout from Settings
2. Login with Remember Me UNCHECKED ❌
3. See: "Token NOT saved (session only)"
4. Press 'R' to hot restart
5. Expected: Shows LoginPage (must login again)

API Response Differences

Login Response

{
  "success": true,
  "data": {
    "access_token": "...",
    "user": {
      "id": "...",
      "email": "...",
      "name": "...",
      "roles": ["admin"],
      "isActive": true,
      "createdAt": "2025-10-10T02:27:42.523Z"  // ✅ Has createdAt
    }
  },
  "message": "Operation successful"
}

Profile Response

{
  "success": true,
  "data": {
    "id": "...",
    "email": "...",
    "name": "...",
    "roles": ["admin"],
    "isActive": true
    // ❌ Missing: createdAt, updatedAt
  }
}

Solution: UserModel now handles both cases gracefully.


Files Modified

lib/features/auth/data/models/user_model.dart

  • Made createdAt optional in fromJson()
  • Defaults to DateTime.now() if missing
  • Made updatedAt optional, defaults to createdAt

lib/features/auth/data/datasources/auth_remote_datasource.dart

  • Added debug logging for profile response
  • Already correctly extracts nested data object

Summary

🎉 Auto-login is now fully working!

The issue was that your backend's /auth/profile endpoint returns a minimal user object without timestamp fields, while the /auth/login endpoint includes them. The UserModel now gracefully handles both response formats.

What Works Now:

Login with Remember Me → Token saved App restart → Token loaded → Profile fetched → Auto-login Login without Remember Me → Token not saved → Must login again Logout → Token cleared → Back to login page


Test It Now!

# Start the app
flutter run

# Login with Remember Me checked
# Close and reopen, or press 'R'
# Should auto-login to MainScreen!

🚀 Auto-login is complete and working!