I’m facing an issue with my Flutter app that uses Firebase Authentication. The problem occurs when a user’s account is deleted from Firebase and then they re-register using the same phone number. After re-registering, the app is incorrectly routing them back to the registration page instead of the home page.
Details
Platform: Flutter
State Management: Riverpod
Authentication: Firebase Authentication (Phone Authentication)
Problem Description
Initial Setup: The app works perfectly for new users. They can register and are routed to the correct page based on their authentication state.
Issue Encountered: When a user’s account is deleted from Firebase and they re-register, the app routes them back to the registration page instead of the home page.
Observation: The issue seems to be specific to re-registered users, as the app works fine for new users and other users whose haven’t got their account deleted from firebase and re-registered their accounts.
Debugging Steps Taken
Cleared Local Data: Cleared app data and cache, and reinstalled the app.
Checked Auth State Changes: Added logs to trace authentication state changes.
Verified User Data: Checked that all necessary user data is correctly initialized and saved in Firebase.
Routing Logic: Verified that the routing logic correctly redirects the user based on their authentication state.
Auth Repository
abstract class AuthRepository {
AppUser? get currentUser;
Stream<AppUser?> authStateChanges();
Future<void> signInWithPhone({
required String phoneNumber,
required Function(PhoneAuthCredential) onVerificationComplete,
});
Future<void> verifyOtp({ required String otp });
Future<String?> getToken();
Future<void> signOut();
}
firebase_auth_repository.dart
class FireBaseAuthRepository implements AuthRepository {
final FirebaseAuth _firebaseAuth;
FireBaseAuthRepository(this._firebaseAuth);
String _verificationId = '';
Future<void> initialize() async {
await _firebaseAuth.setPersistence(Persistence.LOCAL);
}
Future<void> clearLocalData() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
print('Local data cleared');
}
AppUser? _userFromFirebase(User? user) {
if (user == null) {
return null;
}
return AppUser(
uid: user.uid,
phone: user.phoneNumber!,
);
}
@override
AppUser? get currentUser {
final user = _firebaseAuth.currentUser;
print('Current user: ${user?.uid}');
return _userFromFirebase(user);
}
@override
Stream<AppUser?> authStateChanges() {
return _firebaseAuth.authStateChanges().map((user) {
print('Auth state changed: ${user?.uid}');
return _userFromFirebase(user);
});
}
@override
Future<String?> getToken() async {
try {
final token = await _firebaseAuth.currentUser?.getIdToken();
print('Token retrieved: $token');
return token;
} catch (e) {
print('Error getting token: $e');
throw Exception(e);
}
}
@override
Future<void> signInWithPhone({
required String phoneNumber,
required Function(PhoneAuthCredential) onVerificationComplete,
}) async {
String phoneNumberWithCountryCode = '+91$phoneNumber';
await _firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumberWithCountryCode,
verificationCompleted: (PhoneAuthCredential credential) async {
print('Verification completed');
final user = await AsyncValue.guard(() => _firebaseAuth.signInWithCredential(credential));
user.when(data: (data) async {
if (data.user?.uid != "" || data.user != null) {
print('User signed in: ${data.user?.uid}');
await onVerificationComplete(credential);
}
}, error: (error, stackTrace) {
print('Verification error: $error');
}, loading: () {
print('Verification loading');
});
},
verificationFailed: (FirebaseAuthException e) {
print('Verification failed: ${e.message}');
throw Exception(e.message);
},
codeSent: (String verificationId, int? resendToken) async {
print('Code sent: $verificationId');
_verificationId = verificationId;
},
codeAutoRetrievalTimeout: (verificationId) {
print('Code auto retrieval timeout: $verificationId');
},
);
}