在 Firebase 中处理关联帐户
Handling linking accounts in Firebase
我正在按照 Firebase 关于社交登录的说明进行操作。下面是我正在使用的示例,从登录身份验证的角度来看,它一切正常。
但是,我 Google 和 Facebook 登录都是独立工作的。
我现在希望能够做的是 link 帐户。实际上,您可以在下面看到这可能发生的情况(请参阅评论):
If you are using multiple auth providers on your app you should handle linking the user's accounts here.
我已经尝试了很多我认为应该放在这里的变体,但都无济于事。任何人都可以就他们认为应该放在这里的内容指导我吗?谢谢!
function initFBApp() {
// Result from Redirect auth flow.
// [START getidptoken]
firebase.auth().getRedirectResult().then(function (result) {
if (result.credential) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// [START_EXCLUDE]
document.getElementById('FBquickstart-oauthtoken').textContent = token;
}
else {
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// The signed-in user info.
var user = result.user;
}).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// [START_EXCLUDE]
if (errorCode === 'auth/account-exists-with-different-credential') {
alert('You have already signed up with a different auth provider for that emails.');
// If you are using multiple auth providers on your app you should handle linking
// the user's accounts here.
}
else {
console.error(error);
}
// [END_EXCLUDE]
});
// [END getidptoken]
// Listening for auth state changes.
// [START authstatelistener]
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var isAnonymous = user.isAnonymous;
var uid = user.uid;
var providerData = user.providerData;
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed in';
document.getElementById('FBquickstart-sign-in').textContent = 'Log out';
document.getElementById('FBquickstart-account-details').textContent = JSON.stringify(user, null, ' ');
// [END_EXCLUDE]
}
else {
// User is signed out.
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed out';
document.getElementById('FBquickstart-sign-in').textContent = 'Log in with Facebook';
document.getElementById('FBquickstart-account-details').textContent = 'null';
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in').disabled = false;
// [END_EXCLUDE]
});
// [END authstatelistener]
document.getElementById('FBquickstart-sign-in').addEventListener('click', toggleFBSignIn, false);
}
大致的处理步骤如下auth/account-exists-with-different-credential:
如果您登录的新 Facebook 帐户使用的是另一个已存在帐户的电子邮件地址,则会出现该错误。假设现有帐户是 google 帐户。
您将在 getRedirectResult().catch(function(error) {})
中收到该错误
该错误还将包含电子邮件和凭据字段。
您将需要保存凭据(使用推荐的 sessionStorage)。查看此 post 了解更多信息:
然后您调用 firebase.auth().fetchProvidersForEmail(error.email)
以确定该电子邮件已经存在的提供商。
然后您将登录这些现有提供商之一并断言该电子邮件与 error.email 相同。成功后,您将从 sessionStorage 加载挂起的凭据,按照其他 post 和 link 中的描述重新初始化到当前用户:
firebase.auth().currentUser.linkWithCredential(savedCred);
您现在将拥有两个帐户 linked。请记住,现有提供程序可能是密码类型。在这种情况下,您不需要保存凭据,只需询问用户密码并使用相同的电子邮件 error.email 登录即可。然后,您可以使用 error.credential.
直接调用 link
顺便说一句,我推荐 firebaseui-web,它会为您处理所有这一切:
https://github.com/firebase/firebaseui-web
我认为 Firebase API changed a bit 和 firebase.auth().currentUser.link(savedCred);
现在是 firebase.auth().currentUser.linkWithRedirect(provider)
。在我的实现中,我将最初选择的提供程序保存到 sessionStorage,并在需要帐户链接时将其与上述方法一起使用。
如果更适合您的需要,您也可以执行 linkWithPopUp
。
下面是相关的工作代码片段(位于异步函数内)。请注意,"apples" 只是 Firestore 中代表购物车的简化测试记录。
if(error.code === "auth/email-already-in-use"){
// REMEMBER AUTH CURRENT USER OBJECT
previousUser = firebase.auth().currentUser;
// WE MUST HANDLE DB READ AND DELETE WHILE SIGNED IN AS PREVIOUS USER PER FIRESTORE SECURITY RULES
if(localUserDoc){ //this was saved from .snapshot firing
if(localUserDoc.data().apples){
apples = localUserDoc.data().apples;
}
}
//DELETE CURRENT USER RECORD WHILE STILL SIGNED IN
await firebase.firestore().collection("users").doc(previousUser.uid).delete();
// CLEAN UP DONE. NOW SIGN IN USING EMAIL LINK CREDENTIAL
try {
var firebaseUserObj = await firebase.auth().signInAndRetrieveDataWithCredential(credential);
// FIRESTORE USER RECORD FOR EMAIL LINK USER WAS CREATED WHEN THEY ADDED APPLE TO CART
try {
var doc = await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).get();
if (doc.exists) {
if(doc.data().apples){
apples = apples + doc.data().apples;
}
}
await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).update({
apples: apples
});
} catch(error) {
console.log("Error getting document:", error);
}
previousUser.delete();
} catch (error) {
console.log(".signInWithCredential err ", error);
}
}
仔细阅读示例https://firebase.google.com/docs/auth/web/google-signin
部分 "Handling account-exists-with-different-credential Errors"
Redirect mode This error is handled in a similar way in the redirect
mode, with the difference that the pending credential has to be cached
between page redirects (for example, using session storage).
我正在按照 Firebase 关于社交登录的说明进行操作。下面是我正在使用的示例,从登录身份验证的角度来看,它一切正常。
但是,我 Google 和 Facebook 登录都是独立工作的。
我现在希望能够做的是 link 帐户。实际上,您可以在下面看到这可能发生的情况(请参阅评论):
If you are using multiple auth providers on your app you should handle linking the user's accounts here.
我已经尝试了很多我认为应该放在这里的变体,但都无济于事。任何人都可以就他们认为应该放在这里的内容指导我吗?谢谢!
function initFBApp() {
// Result from Redirect auth flow.
// [START getidptoken]
firebase.auth().getRedirectResult().then(function (result) {
if (result.credential) {
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
var token = result.credential.accessToken;
// [START_EXCLUDE]
document.getElementById('FBquickstart-oauthtoken').textContent = token;
}
else {
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// The signed-in user info.
var user = result.user;
}).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// [START_EXCLUDE]
if (errorCode === 'auth/account-exists-with-different-credential') {
alert('You have already signed up with a different auth provider for that emails.');
// If you are using multiple auth providers on your app you should handle linking
// the user's accounts here.
}
else {
console.error(error);
}
// [END_EXCLUDE]
});
// [END getidptoken]
// Listening for auth state changes.
// [START authstatelistener]
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var isAnonymous = user.isAnonymous;
var uid = user.uid;
var providerData = user.providerData;
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed in';
document.getElementById('FBquickstart-sign-in').textContent = 'Log out';
document.getElementById('FBquickstart-account-details').textContent = JSON.stringify(user, null, ' ');
// [END_EXCLUDE]
}
else {
// User is signed out.
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in-status').textContent = 'Signed out';
document.getElementById('FBquickstart-sign-in').textContent = 'Log in with Facebook';
document.getElementById('FBquickstart-account-details').textContent = 'null';
document.getElementById('FBquickstart-oauthtoken').textContent = 'null';
// [END_EXCLUDE]
}
// [START_EXCLUDE]
document.getElementById('FBquickstart-sign-in').disabled = false;
// [END_EXCLUDE]
});
// [END authstatelistener]
document.getElementById('FBquickstart-sign-in').addEventListener('click', toggleFBSignIn, false);
}
大致的处理步骤如下auth/account-exists-with-different-credential: 如果您登录的新 Facebook 帐户使用的是另一个已存在帐户的电子邮件地址,则会出现该错误。假设现有帐户是 google 帐户。
您将在 getRedirectResult().catch(function(error) {})
该错误还将包含电子邮件和凭据字段。
您将需要保存凭据(使用推荐的 sessionStorage)。查看此 post 了解更多信息:
然后您调用 firebase.auth().fetchProvidersForEmail(error.email)
以确定该电子邮件已经存在的提供商。
然后您将登录这些现有提供商之一并断言该电子邮件与 error.email 相同。成功后,您将从 sessionStorage 加载挂起的凭据,按照其他 post 和 link 中的描述重新初始化到当前用户:
firebase.auth().currentUser.linkWithCredential(savedCred);
您现在将拥有两个帐户 linked。请记住,现有提供程序可能是密码类型。在这种情况下,您不需要保存凭据,只需询问用户密码并使用相同的电子邮件 error.email 登录即可。然后,您可以使用 error.credential.
直接调用 link顺便说一句,我推荐 firebaseui-web,它会为您处理所有这一切: https://github.com/firebase/firebaseui-web
我认为 Firebase API changed a bit 和 firebase.auth().currentUser.link(savedCred);
现在是 firebase.auth().currentUser.linkWithRedirect(provider)
。在我的实现中,我将最初选择的提供程序保存到 sessionStorage,并在需要帐户链接时将其与上述方法一起使用。
如果更适合您的需要,您也可以执行 linkWithPopUp
。
下面是相关的工作代码片段(位于异步函数内)。请注意,"apples" 只是 Firestore 中代表购物车的简化测试记录。
if(error.code === "auth/email-already-in-use"){
// REMEMBER AUTH CURRENT USER OBJECT
previousUser = firebase.auth().currentUser;
// WE MUST HANDLE DB READ AND DELETE WHILE SIGNED IN AS PREVIOUS USER PER FIRESTORE SECURITY RULES
if(localUserDoc){ //this was saved from .snapshot firing
if(localUserDoc.data().apples){
apples = localUserDoc.data().apples;
}
}
//DELETE CURRENT USER RECORD WHILE STILL SIGNED IN
await firebase.firestore().collection("users").doc(previousUser.uid).delete();
// CLEAN UP DONE. NOW SIGN IN USING EMAIL LINK CREDENTIAL
try {
var firebaseUserObj = await firebase.auth().signInAndRetrieveDataWithCredential(credential);
// FIRESTORE USER RECORD FOR EMAIL LINK USER WAS CREATED WHEN THEY ADDED APPLE TO CART
try {
var doc = await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).get();
if (doc.exists) {
if(doc.data().apples){
apples = apples + doc.data().apples;
}
}
await firebase.firestore().collection("users").doc(firebaseUserObj.user.uid).update({
apples: apples
});
} catch(error) {
console.log("Error getting document:", error);
}
previousUser.delete();
} catch (error) {
console.log(".signInWithCredential err ", error);
}
}
仔细阅读示例https://firebase.google.com/docs/auth/web/google-signin 部分 "Handling account-exists-with-different-credential Errors"
Redirect mode This error is handled in a similar way in the redirect mode, with the difference that the pending credential has to be cached between page redirects (for example, using session storage).