使用 JavaScript 将多个身份验证提供方与一个帐号相关联

您可以通过将多个身份验证提供方凭据关联至现有用户帐号,让用户可以使用多个身份验证提供方登录您的应用。无论用户使用哪个身份验证提供方登录,系统均可通过同一 Firebase 用户 ID 识别用户。例如,使用密码登录的用户可以关联 Google 帐号,以后便可使用这两种方法中的任意一种登录。或者,匿名用户可以关联 Facebook 帐号,以后就可以使用 Facebook 帐号登录并继续使用您的应用。

准备工作

为您的应用增加对两个或多个身份验证提供方(可能包括匿名身份验证)的支持。

如需将如 Google 或 Facebook 等身份验证提供方的凭据与现有用户帐号关联,请按以下步骤操作:

  1. 使用任意身份验证提供方或方法让用户登录。
  2. 获取要关联至用户帐号的提供方所对应的 AuthProvider 对象。示例:

    Web 模块化 API

    import { GoogleAuthProvider, FacebookAuthProvider, TwitterAuthProvider, GithubAuthProvider } from "firebase/auth";
    
    const googleProvider = new GoogleAuthProvider();
    const facebookProvider = new FacebookAuthProvider();
    const twitterProvider = new TwitterAuthProvider();
    const githubProvider = new GithubAuthProvider();

    Web 命名空间型 API

    var googleProvider = new firebase.auth.GoogleAuthProvider();
    var facebookProvider = new firebase.auth.FacebookAuthProvider();
    var twitterProvider = new firebase.auth.TwitterAuthProvider();
    var githubProvider = new firebase.auth.GithubAuthProvider();
  3. 提示用户使用要关联的提供方进行登录。您可以通过弹出式窗口或重定向至提供方的登录页面,提示用户进行登录。在移动设备上最好使用重定向方法。
    • 如需使用弹出式窗口登录,请调用 linkWithPopup

      Web 模块化 API

      import { getAuth, linkWithPopup, GoogleAuthProvider } from "firebase/auth";
      const provider = new GoogleAuthProvider();
      
      const auth = getAuth();
      linkWithPopup(auth.currentUser, provider).then((result) => {
        // Accounts successfully linked.
        const credential = GoogleAuthProvider.credentialFromResult(result);
        const user = result.user;
        // ...
      }).catch((error) => {
        // Handle Errors here.
        // ...
      });

      Web 命名空间型 API

      auth.currentUser.linkWithPopup(provider).then((result) => {
        // Accounts successfully linked.
        var credential = result.credential;
        var user = result.user;
        // ...
      }).catch((error) => {
        // Handle Errors here.
        // ...
      });
    • 如需重定向到提供方的登录页面进行登录,请调用 linkWithRedirect:使用“linkWithRedirect”时,请遵循最佳实践

      Web 模块化 API

      import { getAuth, linkWithRedirect, GoogleAuthProvider } from "firebase/auth";
      const provider = new GoogleAuthProvider();
      
      const auth = getAuth();
      linkWithRedirect(auth.currentUser, provider)
        .then(/* ... */)
        .catch(/* ... */);

      Web 命名空间型 API

      auth.currentUser.linkWithRedirect(provider)
        .then(/* ... */)
        .catch(/* ... */);
      用户在登录之后会被重定向回您的页面。然后,您可以在页面加载时调用 getRedirectResult 来检索登录结果:

      Web 模块化 API

      import { getRedirectResult } from "firebase/auth";
      getRedirectResult(auth).then((result) => {
        const credential = GoogleAuthProvider.credentialFromResult(result);
        if (credential) {
          // Accounts successfully linked.
          const user = result.user;
          // ...
        }
      }).catch((error) => {
        // Handle Errors here.
        // ...
      });

      Web 命名空间型 API

      auth.getRedirectResult().then((result) => {
        if (result.credential) {
          // Accounts successfully linked.
          var credential = result.credential;
          var user = result.user;
          // ...
        }
      }).catch((error) => {
        // Handle Errors here.
        // ...
      });
    如果用户成功登录,系统就会将用户的提供方帐号与用户在您的 Firebase 项目中的帐号相关联。

    如果凭据已与另一用户帐号关联,帐号关联将失败。在这种情况下,您必须根据需要为您的应用合并帐号和相关数据:

    Web 模块化 API

    import { getAuth, signInWithCredential, linkWithCredential, OAuthProvider } from "firebase/auth";
    
    // The implementation of how you store your user data depends on your application
    const repo = new MyUserDataRepo();
    
    // Get reference to the currently signed-in user
    const auth = getAuth();
    const prevUser = auth.currentUser;
    
    // Get the data which you will want to merge. This should be done now
    // while the app is still signed in as this user.
    const prevUserData = repo.get(prevUser);
    
    // Delete the user's data now, we will restore it if the merge fails
    repo.delete(prevUser);
    
    // Sign in user with the account you want to link to
    signInWithCredential(auth, newCredential).then((result) => {
      console.log("Sign In Success", result);
      const currentUser = result.user;
      const currentUserData = repo.get(currentUser);
    
      // Merge prevUser and currentUser data stored in Firebase.
      // Note: How you handle this is specific to your application
      const mergedData = repo.merge(prevUserData, currentUserData);
    
      const credential = OAuthProvider.credentialFromResult(result);
      return linkWithCredential(prevUser, credential)
        .then((linkResult) => {
          // Sign in with the newly linked credential
          const linkCredential = OAuthProvider.credentialFromResult(linkResult);
          return signInWithCredential(auth, linkCredential);
        })
        .then((signInResult) => {
          // Save the merged data to the new user
          repo.set(signInResult.user, mergedData);
        });
    }).catch((error) => {
      // If there are errors we want to undo the data merge/deletion
      console.log("Sign In Error", error);
      repo.set(prevUser, prevUserData);
    });

    Web 命名空间型 API

    // The implementation of how you store your user data depends on your application
    var repo = new MyUserDataRepo();
    
    // Get reference to the currently signed-in user
    var prevUser = auth.currentUser;
    
    // Get the data which you will want to merge. This should be done now
    // while the app is still signed in as this user.
    var prevUserData = repo.get(prevUser);
    
    // Delete the user's data now, we will restore it if the merge fails
    repo.delete(prevUser);
    
    // Sign in user with the account you want to link to
    auth.signInWithCredential(newCredential).then((result) => {
      console.log("Sign In Success", result);
      var currentUser = result.user;
      var currentUserData = repo.get(currentUser);
    
      // Merge prevUser and currentUser data stored in Firebase.
      // Note: How you handle this is specific to your application
      var mergedData = repo.merge(prevUserData, currentUserData);
    
      return prevUser.linkWithCredential(result.credential)
        .then((linkResult) => {
          // Sign in with the newly linked credential
          return auth.signInWithCredential(linkResult.credential);
        })
        .then((signInResult) => {
          // Save the merged data to the new user
          repo.set(signInResult.user, mergedData);
        });
    }).catch((error) => {
      // If there are errors we want to undo the data merge/deletion
      console.log("Sign In Error", error);
      repo.set(prevUser, prevUserData);
    });

如需将电子邮件地址和密码凭据添加到现有用户帐号,请按以下步骤操作:

  1. 使用任意身份验证提供方或方法让用户登录。
  2. 提示用户输入电子邮件地址和新密码。
  3. 使用电子邮件地址和密码创建一个 AuthCredential 对象:

    Web 模块化 API

    import { EmailAuthProvider } from "firebase/auth";
    
    const credential = EmailAuthProvider.credential(email, password);

    Web 命名空间型 API

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. AuthCredential 对象传递给已登录用户的 linkWithCredential 方法:

    Web 模块化 API

    import { getAuth, linkWithCredential } from "firebase/auth";
    
    const auth = getAuth();
    linkWithCredential(auth.currentUser, credential)
      .then((usercred) => {
        const user = usercred.user;
        console.log("Account linking success", user);
      }).catch((error) => {
        console.log("Account linking error", error);
      });

    Web 命名空间型 API

    auth.currentUser.linkWithCredential(credential)
      .then((usercred) => {
        var user = usercred.user;
        console.log("Account linking success", user);
      }).catch((error) => {
        console.log("Account linking error", error);
      });

    如果凭据已与另一用户帐号关联,则无法调用 linkWithCredential。在这种情况下,您必须根据需要为您的应用合并帐号和相关数据(请参阅上方示例)。

您可以取消身份验证提供方与帐号的关联,这样用户就无法再通过该提供方登录了。

如需解除身份验证提供方与用户帐号的关联,请将提供方 ID 传递给 unlink 方法。您可以从 providerData 属性中获取与用户相关联的身份验证提供方的 ID。

Web 模块化 API

import { getAuth, unlink } from "firebase/auth";

const auth = getAuth();
unlink(auth.currentUser, providerId).then(() => {
  // Auth provider unlinked from account
  // ...
}).catch((error) => {
  // An error happened
  // ...
});

Web 命名空间型 API

user.unlink(providerId).then(() => {
  // Auth provider unlinked from account
  // ...
}).catch((error) => {
  // An error happened
  // ...
});