使用密码身份验证向用户添加电子邮件链接身份验证时,linkWithCredential 会引发提供程序已链接的错误

时间:2021-05-15 23:51:05

标签: firebase firebase-authentication

我有一个使用电子邮件/密码进行身份验证的用户。当我尝试使用 credentialWithLinklinkWithCredential 为用户添加电子邮件链接身份验证时,出现错误:

code: "auth/provider-already-linked"
message: "User can only be linked to one identity for the given provider."

错误消息告诉我,我无法为用户添加电子邮件链接身份验证,因为用户已经使用密码通过电子邮件提供商的身份验证。

那么使用密码认证为现有用户添加 emaillink 认证的正确方法是什么?

这是我尝试抛出“auth/provider-already-linked”错误的尝试:

  1. 使用电子邮件和密码创建用户。发送电子邮件验证
const userCred = await firebase
  .auth()
  .createUserWithEmailAndPassword(email, password);

if (userCred) {
  const { user } = userCred;
  await user.sendEmailVerification());
}
  1. 验证电子邮件
await firebase.auth().applyActionCode(actionCode)
await firebase.auth().currentUser.reload()

此时,用户已登录,fetchSignInMethodsForEmail 返回 ['password']

const signInMethods = await firebase.auth().fetchSignInMethodsForEmail(email)

if (signInMethods) {
  console.log(signInMethods) // returns ['password']
}
  1. 将登录链接发送到电子邮件
await firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
window.localStorage.setItem('emailForSignIn', email)
  1. 确认登录链接
if (firebase.auth().isSignInWithEmailLink(href)) {
  let email = window.localStorage.getItem('emailForSignIn')
  if (!email) {
    email = window.prompt('Please provide your email for confirmation')
  }
  const credential = firebase.auth.EmailAuthProvider.credentialWithLink(
    email,
    window.location.href
  );
  firebase
    .auth()
    .currentUser.linkWithCredential(credential)
    .then((result) => {
      console.log('success linking with email link')
      window.localStorage.removeItem('emailForSignIn')
    })
    .catch((error) => {
      // Some error occurred.
      console.log('error linking with email link')
      console.log(error)
    })
}

由于我已经登录并与电子邮件提供商关联,因此调用 firebase.auth.EmailAuthProvider.credentialWithLink 会引发错误。

那么如何为已经通过电子邮件提供商进行身份验证的用户添加 emaillink 登录方法,该电子邮件提供商只有 password 登录方法?从错误消息中,我知道我不应该使用 credentialWithLinklinkWithCredential。那我该怎么办?

2 个答案:

答案 0 :(得分:1)

您正尝试将使用电子邮件创建的帐户与另一个电子邮件相关联。正如错误所说,“用户只能链接到给定提供者的一个身份。”。这意味着一个帐户只能将一个电子邮件、Google 帐户、Facebook 等链接在一起。

如果您希望用户拥有多封电子邮件,那么您可能必须使用数据库。 enter image description here

如果您使用 Firestore,那么您可以像这样存储电子邮件,并在需要时进行如下查询:

userRef.where("emails", "array-contains", "emailEnteredByUser");

PS:用户只能使用他们最初用于创建帐户的主电子邮件来登录或注册。

解决方法:您实际上可以依靠 Email-Link Authentication 来允许用户登录。当用户输入他们的电子邮件并单击登录时,您可以运行云功能(或您自己服务器中的功能)以生成登录链接,然后验证用户的文档是否包含他们输入的电子邮件并使用类似 {{3 }} 将登录链接通过电子邮件发送给他们。

编辑: OP 假定的密码和登录链接是不同的身份验证提供程序。如文档中所述Nodemailer

<块引用>

用户第一次登录后,一个新的用户帐户 创建并链接到凭据 - 即用户名和 密码、电话号码或身份验证提供商信息——用户签名 在与。 因此,如果用户存在电子邮件,您仍然可以使用电子邮件链接。

答案 1 :(得分:0)

所以我想出了如何向使用电子邮件/密码登录的用户添加电子邮件链接。

TL/DR;使用 firebase.auth().signInWithEmailLink(email, href)。不要使用 credentialWithLinklinkWithCredential

步骤 1 - 3 是正确的,但我需要更改步骤 4 以使用 firebase.auth().signInWithEmailLink(email, href) 而不是 credentialWithLinklinkWithCredential

这是正确的步骤 4:

if (firebase.auth().isSignInWithEmailLink(href)) {
  let email = window.localStorage.getItem('emailForSignIn')
  if (!email) {
    email = window.prompt('Please provide your email for confirmation')
  }

  firebase
    .auth()
    .signInWithEmailLink(email, href)
    .then((result) => {
      console.log('success linking with email link')
      window.localStorage.removeItem('emailForSignIn')
    })
    .catch((error) => {
      // Some error occurred.
      console.log('error signing in with email link')
      console.log(error)
    })
}