Fix anonymous account upgrade to preserve Firebase UID
This commit is contained in:
parent
81064da7f4
commit
0522e6cbbf
|
|
@ -131,6 +131,16 @@ class FirebaseAuthService implements AuthService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _hasAnonymousFirebaseUser =>
|
||||||
|
_firebaseAuth?.currentUser?.isAnonymous == true;
|
||||||
|
|
||||||
|
void _updateAuthenticatedUser(fb.User user) {
|
||||||
|
_updateState(AuthState.authenticated(
|
||||||
|
_mapFirebaseUser(user),
|
||||||
|
isFirebaseConfigured: true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> signInWithEmailAndPassword(String email, String password) async {
|
Future<void> signInWithEmailAndPassword(String email, String password) async {
|
||||||
final auth = _firebaseAuth;
|
final auth = _firebaseAuth;
|
||||||
|
|
@ -149,13 +159,29 @@ class FirebaseAuthService implements AuthService {
|
||||||
));
|
));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final normalizedEmail = email.trim();
|
||||||
|
|
||||||
|
// If the current Firebase identity is anonymous, upgrade that identity
|
||||||
|
// instead of signing into a new Firebase UID. This preserves ownership
|
||||||
|
// of any cloud data already associated with the guest account.
|
||||||
|
if (_hasAnonymousFirebaseUser) {
|
||||||
|
final credential = fb.EmailAuthProvider.credential(
|
||||||
|
email: normalizedEmail,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
final result = await auth.currentUser!.linkWithCredential(credential);
|
||||||
|
if (result.user != null) {
|
||||||
|
_updateAuthenticatedUser(result.user!);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final credential = await auth.signInWithEmailAndPassword(
|
final credential = await auth.signInWithEmailAndPassword(
|
||||||
email: email.trim(),
|
email: normalizedEmail,
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
if (credential.user != null) {
|
if (credential.user != null) {
|
||||||
final user = _mapFirebaseUser(credential.user!);
|
_updateAuthenticatedUser(credential.user!);
|
||||||
_updateState(AuthState.authenticated(user, isFirebaseConfigured: true));
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_updateState(AuthState.error(
|
_updateState(AuthState.error(
|
||||||
|
|
@ -188,16 +214,29 @@ class FirebaseAuthService implements AuthService {
|
||||||
));
|
));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final credential = await auth.createUserWithEmailAndPassword(
|
final normalizedEmail = email.trim();
|
||||||
email: email.trim(),
|
final emailCredential = fb.EmailAuthProvider.credential(
|
||||||
|
email: normalizedEmail,
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fb.UserCredential credential;
|
||||||
|
if (_hasAnonymousFirebaseUser) {
|
||||||
|
// Upgrade the existing anonymous UID rather than creating a second
|
||||||
|
// account. This keeps prior guest-owned cloud data attached to the user.
|
||||||
|
credential = await auth.currentUser!.linkWithCredential(emailCredential);
|
||||||
|
} else {
|
||||||
|
credential = await auth.createUserWithEmailAndPassword(
|
||||||
|
email: normalizedEmail,
|
||||||
|
password: password,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (credential.user != null) {
|
if (credential.user != null) {
|
||||||
if (displayName != null && displayName.trim().isNotEmpty) {
|
if (displayName != null && displayName.trim().isNotEmpty) {
|
||||||
await credential.user!.updateDisplayName(displayName.trim());
|
await credential.user!.updateDisplayName(displayName.trim());
|
||||||
}
|
}
|
||||||
final user = _mapFirebaseUser(credential.user!);
|
_updateAuthenticatedUser(credential.user!);
|
||||||
_updateState(AuthState.authenticated(user, isFirebaseConfigured: true));
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_updateState(AuthState.error(
|
_updateState(AuthState.error(
|
||||||
|
|
@ -231,7 +270,14 @@ class FirebaseAuthService implements AuthService {
|
||||||
|
|
||||||
if (googleUser == null) {
|
if (googleUser == null) {
|
||||||
// User cancelled the sign-in modal/flow
|
// User cancelled the sign-in modal/flow
|
||||||
_updateState(_currentState.copyWith(status: _currentState.user != null ? AuthStatus.authenticated : AuthStatus.unauthenticated, clearError: true));
|
_updateState(_currentState.copyWith(
|
||||||
|
status: _currentState.user != null
|
||||||
|
? (_currentState.user!.isAnonymous
|
||||||
|
? AuthStatus.anonymous
|
||||||
|
: AuthStatus.authenticated)
|
||||||
|
: AuthStatus.unauthenticated,
|
||||||
|
clearError: true,
|
||||||
|
));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,10 +287,17 @@ class FirebaseAuthService implements AuthService {
|
||||||
idToken: googleAuth.idToken,
|
idToken: googleAuth.idToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await auth.signInWithCredential(credential);
|
fb.UserCredential result;
|
||||||
|
if (_hasAnonymousFirebaseUser) {
|
||||||
|
// Critical: link the Google provider to the existing anonymous user.
|
||||||
|
// Do not call signInWithCredential here or the guest UID can be replaced.
|
||||||
|
result = await auth.currentUser!.linkWithCredential(credential);
|
||||||
|
} else {
|
||||||
|
result = await auth.signInWithCredential(credential);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.user != null) {
|
if (result.user != null) {
|
||||||
final user = _mapFirebaseUser(result.user!);
|
_updateAuthenticatedUser(result.user!);
|
||||||
_updateState(AuthState.authenticated(user, isFirebaseConfigured: true));
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_updateState(AuthState.error(
|
_updateState(AuthState.error(
|
||||||
|
|
@ -358,7 +411,11 @@ class FirebaseAuthService implements AuthService {
|
||||||
case 'invalid-credential':
|
case 'invalid-credential':
|
||||||
return 'Invalid credentials. Please verify your email and password.';
|
return 'Invalid credentials. Please verify your email and password.';
|
||||||
case 'email-already-in-use':
|
case 'email-already-in-use':
|
||||||
return 'An account already exists with this email address.';
|
return 'An account already exists with this email address. Sign in to that account instead.';
|
||||||
|
case 'credential-already-in-use':
|
||||||
|
return 'This Google account is already linked to another MotorSignal account. Sign in with that account instead of replacing the current guest account.';
|
||||||
|
case 'provider-already-linked':
|
||||||
|
return 'This sign-in provider is already linked to the current account.';
|
||||||
case 'invalid-email':
|
case 'invalid-email':
|
||||||
return 'The email address format is invalid.';
|
return 'The email address format is invalid.';
|
||||||
case 'weak-password':
|
case 'weak-password':
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue