问题
I am following Firebase's instruction on social login. Below is an example of what I am using and it is all working fine from a login authentication perspective.
I have, however, both Google and Facebook login working independently.
What I would like now to be able to do is link the accounts. You can see below in fact where this might go (see the comment):
If you are using multiple auth providers on your app you should handle linking the user's accounts here.
I have tried many variations of what I think should go here, but to no avail. Can anyone guide me in relation to what they think should go here? Thanks!
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);
}
回答1:
These are roughly the steps on how to handle auth/account-exists-with-different-credential: You will get that error if you are signing in to a new Facebook account that uses the email of another account that already exists. Let's say the existing account is a google account.
You will get that error in getRedirectResult().catch(function(error) {})
The error will also contain an email and credential field. You will need to save the credential (using the recommended sessionStorage). Check this post for more on that: Firebase Authentication Javascript: setCookie for pending Credential for redirect
You then call firebase.auth().fetchProvidersForEmail(error.email)
to determine the providers that already exist for that email.
You will then sign in to one of those existing providers and assert that the email is the same as error.email. On success, you will load the pending credential from sessionStorage, re-initialize as described in the other post and link it to the currentUser:
firebase.auth().currentUser.linkWithCredential(savedCred);
You will now have both accounts linked. Keep in mind the existing provider could be a password type. In that case you don't need to save the credential, you just ask the user for the password and sign them in using the same email error.email. You can then call link directly with the error.credential.
BTW, I recommend firebaseui-web which takes care of all this for you: https://github.com/firebase/firebaseui-web
回答2:
I think the Firebase API changed a bit and firebase.auth().currentUser.link(savedCred);
is now firebase.auth().currentUser.linkWithRedirect(provider)
. In my implementation I'm saving the initially selected provider to sessionStorage and use that with the above method in case account linking is required.
You can also do linkWithPopUp
if that suits your needs better.
回答3:
read example carefully https://firebase.google.com/docs/auth/web/google-signin section "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).
回答4:
Below is the relevant snippet of working code (this sits inside an async function). Note that "apples" is just a simplified test record in Firestore representing a shopping cart.
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://stackoverflow.com/questions/39459524/handling-linking-accounts-in-firebase