Javascript Firebase JS API验证-存在具有不同凭据的帐户
我们在试图解决这个问题时遇到了实际问题,因此希望得到Firebase的帮助/那些解决了同样问题的人 该应用程序是React本机(0.43.2)并使用Firebase JS API(最新版本) 我们提供Facebook和谷歌认证。很好 但是,如果用户:Javascript Firebase JS API验证-存在具有不同凭据的帐户,javascript,firebase,firebase-authentication,Javascript,Firebase,Firebase Authentication,我们在试图解决这个问题时遇到了实际问题,因此希望得到Firebase的帮助/那些解决了同样问题的人 该应用程序是React本机(0.43.2)并使用Firebase JS API(最新版本) 我们提供Facebook和谷歌认证。很好 但是,如果用户: 使用Facebook登录(可以) 稍后,登录谷歌(也可以) 稍后,尝试登录Facebook-BOOM!不太好,Firebase返回以下错误: auth/帐户使用不同的凭据存在 通过阅读文档和一些文章,我们认为以下内容是正确的,但显然不是,因为我们得
auth/帐户使用不同的凭据存在
通过阅读文档和一些文章,我们认为以下内容是正确的,但显然不是,因为我们得到了相同的auth错误
...error returned by Firebase auth after trying Facebook login...
const email = error.email;
const pendingCred = error.credential;
firebase.auth().fetchProvidersForEmail(email)
.then(providers => {
//providers returns this array -> ["google.com"]
firebase.auth().signInWithCredential(pendingCred)
.then(result => {
result.user.link(pendingCred)
})
.catch(error => log(error))
对signInWithCredential的调用引发了相同的错误auth/帐户存在于不同的凭据中
有人能帮我们指出这个实现的错误吗?非常感谢。由于谷歌是@gmail.com地址的可信提供商,它比其他使用gmail作为电子邮件的帐户具有更高的优先级。这就是为什么如果你登录Facebook,Gmail不会抛出一个错误,但是如果你尝试登录Facebook,Gmail会抛出一个错误
看
如果您想允许多个帐户使用相同的电子邮件,请转到Firebase控制台,并在“身份验证->登录方法”下,底部应该有一个选项来切换此选项。发生的情况是Firebase对所有电子邮件强制使用相同的帐户。由于同一封电子邮件已经有一个Google帐户,您需要将该Facebook帐户链接到Google帐户,以便用户可以访问相同的数据,下次可以使用Google或Facebook登录到相同的帐户
代码段中的问题是您正在使用相同的凭据进行签名和链接。修改如下。
当您收到错误“身份验证/帐户使用不同凭据存在”时,
错误将包含error.email和error.credential(Facebook OAuth凭据)。您需要首先查找error.email以获取现有提供程序
firebase.auth().fetchProvidersForEmail(error.email)
.then(providers => {
//providers returns this array -> ["google.com"]
// You need to sign in the user to that google account
// with the same email.
// In a browser you can call:
// var provider = new firebase.auth.GoogleAuthProvider();
// provider.setCustomParameters({login_hint: error.email});
// firebase.auth().signInWithPopup(provider)
// If you have your own mechanism to get that token, you get it
// for that Google email user and sign in
firebase.auth().signInWithCredential(googleCred)
.then(user => {
// You can now link the pending credential from the first
// error.
user.linkWithCredential(error.credential)
})
.catch(error => log(error))
我发现Firebase选择这种行为作为默认行为既奇怪又不方便,而且解决方案非常简单。以下是截至本文撰写之时,基于@bojeil的答案,Firebase的完整更新解决方案
function getProvider(providerId) {
switch (providerId) {
case firebase.auth.GoogleAuthProvider.PROVIDER_ID:
return new firebase.auth.GoogleAuthProvider();
case firebase.auth.FacebookAuthProvider.PROVIDER_ID:
return new firebase.auth.FacebookAuthProvider();
case firebase.auth.GithubAuthProvider.PROVIDER_ID:
return new firebase.auth.GithubAuthProvider();
default:
throw new Error(`No provider implemented for ${providerId}`);
}
}
const supportedPopupSignInMethods = [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.FacebookAuthProvider.PROVIDER_ID,
firebase.auth.GithubAuthProvider.PROVIDER_ID,
];
async function oauthLogin(provider) {
try {
await firebase.auth().signInWithPopup(provider);
} catch (err) {
if (err.email && err.credential && err.code === 'auth/account-exists-with-different-credential') {
const providers = await firebase.auth().fetchSignInMethodsForEmail(err.email)
const firstPopupProviderMethod = providers.find(p => supportedPopupSignInMethods.includes(p));
// Test: Could this happen with email link then trying social provider?
if (!firstPopupProviderMethod) {
throw new Error(`Your account is linked to a provider that isn't supported.`);
}
const linkedProvider = getProvider(firstPopupProviderMethod);
linkedProvider.setCustomParameters({ login_hint: err.email });
const result = await firebase.auth().signInWithPopup(linkedProvider);
result.user.linkWithCredential(err.credential);
}
// Handle errors...
// toast.error(err.message || err.toString());
}
}
我给Firebase支持人员发了电子邮件,他们向我解释了更多。用他们自己的话来说:
为了提供上下文,不同的电子邮件都有自己的身份提供者。如果用户的电子邮件为sample@gmail.com,该电子邮件的IDP(身份提供者)将是谷歌,由域名@gmail.com指定(域名为@mycompany.com或@yahoo.com的电子邮件不属于这种情况)
Firebase身份验证允许在检测到使用的提供商是电子邮件的IDP时进行登录,而不管他们是否使用“每个电子邮件地址一个帐户”设置,并且是否已与以前的提供商(如基于电子邮件/密码的身份验证)或任何联合身份提供商(如Facebook)登录。这意味着如果他们登录sample@gmail.com通过电子邮件和密码,然后谷歌(在每个电子邮件地址设置一个帐户的情况下),Firebase将允许后者,该帐户的提供商将更新到谷歌。这样做的原因是,IDP很可能拥有有关电子邮件的最新信息
另一方面,如果他们先使用谷歌登录,然后使用电子邮件和密码帐户登录,并使用相同的关联电子邮件,我们将不希望更新他们的IDP,并将继续默认行为,即通知用户已经是与该电子邮件关联的帐户
我在这里写过如何在不需要第二次登录的情况下执行此操作:
在链接帐户之前,您需要存储原始凭证并检索以静默登录。链接中的完整代码:
signInOrLink: async function (provider, credential, email) {
this.saveCredential(provider, credential)
await auth().signInWithCredential(credential).catch(
async (error) => {
try {
if (error.code != "auth/account-exists-with-different-credential") {
throw error;
}
let methods = await auth().fetchSignInMethodsForEmail(email);
let oldCred = await this.getCredential(methods[0]);
let prevUser = await auth().signInWithCredential(oldCred);
auth().currentUser.linkWithCredential(credential);
}
catch (error) {
throw error;
}
}
);
}有时候firebase文档很棒,有时候它会让您想要更多。在这种情况下,当涉及到处理错误时,它会给出关于使用弹出菜单登录的非常详细的说明。但是,带重定向的signInWithRedirect
的全部说明是
重定向模式
此错误在重定向模式中以类似的方式处理,不同之处在于必须在页面重定向之间缓存挂起的凭据(例如,使用会话存储)
根据@bojeil和@Dominic的回答,以下是如何将facebook帐户与调用signInWithRedirect
的google帐户链接
const提供程序={
google:new firebase.auth.GoogleAuthProvider(),
facebook:new firebase.auth.FacebookAuthProvider(),
twitter:new firebase.auth.TwitterAuthProvider(),
};
const handleAuthError=异步(错误)=>{
if(error.email&&error.credential&&error.code===='auth/account以不同的凭据存在'){
//我们需要保留对存储在'error.credential'中的凭据的访问权限`
//文档建议我们使用会话存储,因此我们将这样做。
setItem('credential',JSON.stringify(error.credential));
const signingmethods=wait firebase.auth().fetchsigningmethodsforemail(error.email);//->['google.com']
const providerKey=signingmethods[0]。拆分('.')[0];/->“谷歌”
const provider=providers[providerKey];//->providers.google
firebase.auth().signInWithRedirect(提供程序);
}
};
const handleRedirect=async()=>{
试一试{
const result=await firebase.auth().getRedirectResult();
const savedCredential=sessionStorage.getItem('credential');
//我们在会话存储中找到保存的凭据
if(result.user&&savedCredential){
handleLinkAccounts(result.user,savedCredential);
}
返回结果;
}
捕获(错误){
handleAuthError(错误);
}
};
const handleLinkAccounts=(authUser,savedCredential)=>{
//火基h