I am writing react native project, where users can sign in with their username
and password
. As firebase authentication does not have the functionality to sign in with username
and password
I do a bit of a trick. On registration I store user's email
in my Users
collection along with other useful users info. So when a user tries to sign in with username
and password, I go to my Users
collection, look for the respective username
, and fetch the corresponding email
address. So in disguise, I use firestore method .signInWithEmailAndPassword(email, password)
. Not ideal, but it does the job.
However, the problem is when a user wants to update his/hers email address to a new one. Then, I surely want to update the email
value within my users collection, so I can perform proper sign in. Here is the code to update user's email:
const onChangeEmail = async () => {
await firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(async function (userCredential) {
await userCredential.user.updateEmail(newEmail).then(async () => {
await updateEmailInFirestore(newEmail);
});
})
.catch((error) => {
console.log("Error while updating email: ", error);
});
};
Where updateEmailInFirestore
does the following:
export const updateEmailInFirestore = async (newEmail) => {
if (!newEmail) {
return;
}
await firebase
.firestore()
.collection("Users")
.doc(firebase.auth().currentUser.uid)
.update({ email: newEmail })
.then(() => console.log("email was updated in user collection: ", newEmail))
.catch((error) => console.log("error while updating email: ", error));
};
The above code works fine and does the job. However, the problem I am having is the following scenario: what if userCredential.user.updateEmail
manages to execute successfully, but then updateEmailInFirestore
fails or throws an exception? Then the value, which I have in my database wont be consistent with the user's updated email address and my login will fail misserably.
Is there a way that I can execute both userCredential.user.updateEmail
and updateEmailInFirestore
as batch operation, so that either both succeed or both fail? I have written batch operations before but they are along the lines of const batch = firebase.firestore().batch();
and my first operation is not related to firestore, but to firebase authentication?
CodePudding user response:
There's nothing much you can do when it comes to ensuring both the promises are resolved or not. Firebase Auth and Firestore are 2 different products so there is no concept like batched write. You can use a Promise.all()
and ask user to retry in case either of the promises fail as shown below.
const updateUserEmail = async () => {
try {
await Promise.all([
user.updateEmail(newEmail),
userDoc.update({
email: newEmail
})
])
} catch (e) {
console.log("Updating email failed")
// prompt user to retry
}
}
A better and widely used solution is to create an email of format [email protected]
and use that with createUserWithEmailAndPassword()
. When a user updates their username (if you want to allow that functionality), then you just have to update the email to [email protected]
. You won't need Firestore for email if you use this method.
Do note that you won't be able to use functionalities like verify email as these email do not exist.