Firestore + AngularFire - Link 个帐户

Firestore + AngularFire - Link accounts

我想为用户提供多种登录选项。目前 Google 和 Facebook。 但是,当用户使用 Google 登录并稍后尝试使用 Facebook 登录时,出现错误:

An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address.

所以我必须 link 帐户。除了 this which didn't help much. The best info I could find is here on 之外,我没有找到任何相关文档,但出于某种原因,这对我来说还不够。 该解决方案处理 signInWithPopup 这给了我这个错误:

"Unable to establish a connection with the popup. It may have been blocked by the browser." So if the popup doesn't work, I call signInWithRedirect:

private authLoginWithPopup(provider: AuthProvider) {
  this.afAuth.auth.signInWithPopup(provider)
    .then((result) => {
      this.credential = result.credential;
      this.updateUserData(result);
    })
    .catch(error => {
      if (error.code === 'auth/account-exists-with-different-credential') {
        this.mergeCredentials(error);
      } else if (error.code === 'auth/popup-blocked') {
        this.authLoginWithRedirect(new firebase.auth.GoogleAuthProvider());
      }
    });
}

Popup 之后,您将进入 then 并可以做任何事情。但是在重定向之后你会丢失你的信息。所以我把它存储在 localStorage:

this.afAuth.auth.fetchProvidersForEmail(error.email).then(providers => {
  const [ provider ] = providers;
  if (provider === 'google.com') {
    const googleProvider = new firebase.auth.GoogleAuthProvider();
    googleProvider.setCustomParameters({ login_hint: error.email });
    localStorage.setItem('auth_error', JSON.stringify(error.credential));
    this.authLoginWithPopup(googleProvider);
  }
});

然后我在 getRedirectResult 中检索它:

constructor(
  private afAuth: AngularFireAuth,
  private afs: AngularFirestore,
) {
  this.afAuth.auth.getRedirectResult().then(result => {
    if (result.user) {
      this.credential = result.credential;
      const authError = JSON.parse(localStorage.getItem('auth_error'));

      if (authError) {
        localStorage.removeItem('auth_error');

        this.afAuth.auth.signInWithCredential(
          firebase.auth.GoogleAuthProvider.credential(
            null, (<any>this.credential).accessToken
          )
        )
          .then(user => {
            user.linkWithCredential(authError)
          })
          .catch(error => {
            console.log('error', error);
          });
      }
    }
  });
}

(这会产生另一个问题。我如何知道它是否是直接使用 Google 登录的用户的正常 signInWithRedirect。或者它是否是用户尝试后的重定向使用 Facebook 登录并因为这个错误而被提示使用 Google 登录?我不想每次都合并凭据。但这是一个较小的问题。但我也很想在这里解决这个问题。 )

但是 signInWithCredential 给我这个错误:

linkWithCredential failed: First argument "credential" must be a valid credential.

我找到了 "solution" here:

When calling signInWithCredential with a access token you need to call it like this:

signInWithCredential(null, token); If you are doing signInWithCredential(token) then you need to use an ID Token rather than an access token.

两个版本我都试过了,都没有成功。第二个版本: this.afAuth.auth.signInWithCredential(firebase.auth.GoogleAuthProvider.credential((<any>this.credential).idToken)) 类型转换是因为我猜类型定义有问题。

我尝试插入整个 credentials 对象,idToken 作为第一个参数,accessToken 作为第二个参数。但我总是得到: 第一个参数 "credential" 必须是有效的凭据。

当我写这篇文章时,我突然想到这可能是 linkWithCredential 而不是 signInWithCredential 方法的问题。根据错误消息,这应该是显而易见的。我只是没有注意到它,因为 google 搜索错误给了我那个解决方案。所以我很快检查了 authError 变量中的内容。有 accessTokenproviderIdsignInMethod 方法。没有tokenId。所以我尝试发送 accessToken 作为参数,而不是整个对象,但没有帮助。我试图将其发送为 null, accessToken。没有帮助。我试图找到 linkWithCredential 方法的文档,但没有任何运气。所以我不知道它在期待什么。

我已经在这上面浪费了四天时间。这让我付出了很大的心理健康代价。有人可以帮我吗?

您可以存储基础 OAuth 凭据(id 令牌或访问令牌):error.credential.idTokenerror.credential.accessToken 及其提供者 ID error.credential.providerId。在 return 页面上,您可以重新初始化凭据,例如:firebase.auth.FacebookAuthProvider.credential(accessToken)。使用 Google、link 凭据

成功重定向登录后