使用 JavaScript 將多個驗證提供者連結至帳戶

您可以將驗證提供者憑證連結至現有的使用者帳戶,讓使用者透過多個驗證服務提供者登入您的應用程式。無論用來登入的驗證服務供應商為何,系統都會透過相同的 Firebase 使用者 ID 來識別使用者。舉例來說,使用密碼登入的使用者可以連結 Google 帳戶,日後再利用任一方法登入。或者,匿名使用者也可以連結 Facebook 帳戶,之後登入 Facebook 繼續使用您的應用程式。

事前準備

在應用程式中新增支援兩個以上驗證供應商 (可能包括匿名驗證) 的支援。

將驗證提供者 (例如 Google 或 Facebook) 的憑證連結至現有的使用者帳戶:

  1. 使用任何驗證提供者或方式登入使用者。
  2. 根據要連結至使用者帳戶的提供者,取得相對應的 AuthProvider 物件。範例:

    網頁模組 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();

    網路命名空間 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

      網頁模組 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.
        // ...
      });

      網路命名空間 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」時,請遵循最佳做法

      網頁模組 API

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

      網路命名空間 API

      auth.currentUser.linkWithRedirect(provider)
        .then(/* ... */)
        .catch(/* ... */);
      使用者登入後,將重新導回您的網頁。然後,您可以在網頁載入時呼叫 getRedirectResult 來擷取登入結果:

      網頁模組 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.
        // ...
      });

      網路命名空間 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 專案中的使用者帳戶。

    如果憑證已連結至另一個使用者帳戶,帳戶連結就會失敗。在這種情況下,您必須視情況處理應用程式的帳戶和關聯資料合併作業:

    網頁模組 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);
    });

    網路命名空間 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 物件:

    網頁模組 API

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

    網路命名空間 API

    var credential = firebase.auth.EmailAuthProvider.credential(email, password);
  4. AuthCredential 物件傳遞至已登入使用者的 linkWithCredential 方法:

    網頁模組 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);
      });

    網路命名空間 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。

網頁模組 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
  // ...
});

網路命名空間 API

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