Firebase LinkWithCredential 保留原有主邮箱地址指南
2024-03-19 00:11:10
通过 Firebase linkWithCredential 保留主要电子邮件
导言
Firebase linkWithCredential
方法提供了一种将用户帐户与多个身份提供者关联的方法。然而,默认情况下,此方法会自动更新用户的主要电子邮件地址,即使未进行电子邮件验证。对于希望保留 Google 提供者电子邮件地址的用户来说,这是一个问题。
问题
当通过密码提供者将用户与 Google 提供者链接时,linkWithCredential
方法会将 Google 提供者的电子邮件地址设置为主要电子邮件。这导致无法在取消链接 Google 提供者或密码提供者后恢复到以前的电子邮件地址。
解决方案
链接电子邮件和密码
为了链接电子邮件和密码而不更新主要电子邮件地址,请执行以下步骤:
- 创建一个新的
EmailAuthProvider
凭证。 - 使用
linkWithCredential
方法将凭证链接到当前用户。 - 发送电子邮件验证以确认新电子邮件地址。
- 在 Firestore 中更新用户文档,将电子邮件更新为新电子邮件。
设置主要电子邮件
在链接新电子邮件后,可以将其设置为主要电子邮件:
- 如果新主要电子邮件来自 Google,请先重新验证用户。
- 使用
updateEmail
方法更新电子邮件地址。 - 在 Firestore 中更新用户文档,将电子邮件更新为新电子邮件。
取消链接提供者
要取消链接提供者,请执行以下步骤:
- 确保用户已链接多个提供者。
- 使用
unlink
方法取消链接提供者。 - 如果取消链接的提供者是密码提供者,请尝试将电子邮件恢复为 Google 提供者的电子邮件地址(如果存在)。
代码示例
以下代码示例演示了如何实现上述方法:
// 链接电子邮件和密码
async function linkEmailAndPassword(newEmail, newPassword) {
const currentUser = getAuth().currentUser;
if (currentUser) {
try {
console.log(`Linking new email: ${newEmail}`);
const credential = EmailAuthProvider.credential(newEmail, newPassword);
await linkWithCredential(currentUser, credential);
console.log(`Email linked successfully, sending verification email.`);
// Send email verification
await sendEmailVerification(currentUser);
// Update Firestore
const userDocRef = doc(firestore, "users", currentUser.uid);
await updateDoc(userDocRef, {
email: newEmail, // Update Firestore with the new email
emailVerified: false, // Set emailVerified to false until the user verifies it
});
// Refresh local state
authStore.setUser({
...authStore.user,
email: newEmail,
emailVerified: false,
});
return {
success: true,
message: "New email linked successfully. Please verify your email.",
};
} catch (error) {
console.error(`Error linking new email: ${error}`);
throw error;
}
} else {
throw new Error("No user currently signed in.");
}
}
// 设置主要电子邮件
async function makeEmailPrimary(newEmail) {
const currentUser = getAuth().currentUser;
if (currentUser && newEmail !== currentUser.email) {
try {
// Reauthentication should have just occurred before this action is triggered.
await updateEmail(currentUser, newEmail);
// Update Firestore and local authStore only after successful email update.
const userDocRef = doc(firestore, "users", currentUser.uid);
await updateDoc(userDocRef, { email: newEmail });
authStore.setUser({ ...authStore.user, email: newEmail }); // Update the local state.
return { success: true, message: "Primary email updated successfully." };
} catch (error) {
console.error(`Error updating primary email: ${error}`);
return { success: false, message: `Failed to update primary email: ${error.message}` };
}
} else {
return {
success: false,
message: "New email is the same as the current one or no user signed in.",
};
}
}
// 取消链接提供者
async function unlinkProvider(providerId) {
console.log(`Attempting to unlink provider: ${providerId}`);
try {
if (authStore.user.providerData.length > 1) {
await unlink(authFirebase.currentUser, providerId);
console.log(`Provider ${providerId} unlinked successfully`);
if (providerId === "password") {
const googleProviderData = authStore.user.providerData.find(
(pd) => pd.providerId === "google.com"
);
if (googleProviderData && googleProviderData.email) {
try {
// Attempt to update the email back to the Google one.
await updateEmail(authFirebase.currentUser, googleProviderData.email);
console.log(
`Firebase Auth email reverted to Google email: ${googleProviderData.email}`
);
await authStore.updateUserInfo({ email: googleProviderData.email });
} catch (error) {
if (error.code === "auth/requires-recent-login") {
console.log("User needs to re-authenticate to update their email.");
} else {
console.error(`Error updating email: ${error}`);
}
}
} else {
console.log("Google email not found or user not signed in with Google.");
}
}
successMessage.value = `Successfully unlinked ${getProviderName(providerId)}`;
await authStore.refreshUser(); // Refresh local user data to reflect changes
} else {
console.log("Cannot unlink the last remaining provider");
error.value = "You cannot unlink the last remaining provider.";
}
} catch (err) {
console.error(`Error unlinking ${getProviderName(providerId)}:`, err);
error.value = `Failed to unlink ${getProviderName(providerId)}. Please try again.`;
}
}
结论
通过使用上述方法,可以链接新身份提供者而不更新主要电子邮件地址,并在取消链接后优雅地恢复到先前方法或替代方法。这提供了更高的灵活性,并确保用户的电子邮件地址始终受到控制。
常见问题解答
-
我可以在
linkWithCredential
中同时更新电子邮件和密码吗?可以,但在更新密码之前必须先验证电子邮件。
-
如何验证新的电子邮件地址?
通过
sendEmailVerification
方法发送电子邮件验证并单击电子邮件中的链接进行验证。 -
取消链接密码提供者后,为什么我需要重新验证?
取消链接密码提供者会将用户的状态重置为未验证,因此需要重新验证才能继续进行其他操作。
-
如果我忘记了新密码,如何恢复我的帐户?
可以使用
sendPasswordResetEmail
方法重置密码。 -
我在尝试使用提供的代码示例时遇到错误,该怎么办?
确保将代码更新为与 Firebase 当前 SDK 版本兼容的代码。如有任何疑问,请查阅 Firebase 文档或在 Stack Overflow 等论坛上寻求帮助。