As part of building out a production app, I have a way to change a user's email address. I do this using
firebase.auth().updateEmail(newEmail);
This works well, and I am sent a email that says that my email has been changed. My issue is, I also get sent a link that allows me to recover my old email (a handy feature for security etc), but that causes the issue where the email address I store can get out of sync.
I store information about the user in a collection, when they change their email address using the above code, I also update the collection with their new email.
The problem is, if they click the recover link, there email address is reset back on on the user authentication side of things, but is not reset on my collection.
What I have been trying to find is something that behaves like this, but I cant find it
functions.auth.user().onEmailChange(async (newEmail, context) =>
{
admin.firestore().collection(Collections.USERS_PRIVATE).doc(context.params.uid).update({ email: newEmail });
});
That way I can mirror my collection to always be in sync with the user authentication email address.
I handle this problem with custom email action handlers and Cloud Functions. I only call the Cloud Function to update the email in Firestore as soon as the user has verified his new authentication email address. Therefore, you will only have valid and verified email addresses in Firestore. This means that if a user changes his email address with firebase.auth().updateEmail(newEmail);, but decides to revert back to his old authentication email through the recoverEmail action, you don´t have to do anything.
Define your custom action url for automatically sent emails by Firebase in the Firebase Console e.g. https://example.com/__/auth/action. Firebase will append all necessary properties to your url such as the oobCode to verify a valid action and the mode which varies between:
resetPassword,recoverEmail orverifyEmailfunction handleVerifyEmail(auth, actionCode, continueUrl, lang) {
// Try to apply the email verification code.
auth.applyActionCode(actionCode).then(function(resp) {
// Email address has been verified.
// TODO: Display a confirmation message to the user.
// TODO: Call your Cloud Function to update the email property in Firestore
}).catch(function(error) {
// Code is invalid or expired. Ask the user to verify their email address
// again.
});
}
Create a Cloud Function with the trigger https.onCall or https.onRequest, call it after a successful email verification and update the email field in Firestore.
Edit #1:
While this solution would work, I still couldn´t find a way to secure the Cloud Function during the verifyEmail process since there is a chance that the user isn´t logged in and I don´t know how to retrieve the uid during this step. I tried the same process with the recoverEmail handler, but ended up having the same issue. I created a post for this security issue, so maybe you will also benefit from it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With