透過 FirebaseUI 輕鬆將登入程序新增至網頁應用程式

FirebaseUI 是建構在 Firebase Authentication SDK 之上的程式庫,可提供用於應用程式的內建 UI 流程。FirebaseUI 具有下列優點:

  • 多個供應商:登入電子郵件/密碼、電子郵件連結、電話驗證、Google、Facebook、Twitter 和 GitHub 登入流程。
  • 帳戶連結:在不同識別資訊提供者間安全連結使用者帳戶的流程。
  • 自訂:根據應用程式需求覆寫 FirebaseUI 的 CSS 樣式。此外,由於 FirebaseUI 是開放原始碼,因此您可以建立專案分支,並完全根據自身需求自訂。
  • 輕觸一下即可註冊和自動登入:自動整合一鍵註冊功能,提供快速的跨裝置登入體驗。
  • 本地化 UI - 支援超過 40 種語言
  • 升級匿名使用者 - 透過登入/註冊的方式升級匿名使用者。詳情請參閱「升級匿名使用者」一節。

事前準備

  1. 將 Firebase 驗證新增至您的網頁應用程式,確保您使用的是 v9 Compat (建議做法) 或舊版 SDK (請參閱上方的側欄)。

  2. 透過下列其中一個選項納入 FirebaseUI:

    1. CDN

      在 Firebase 控制台的初始化程式碼片段下方,將下列指令碼和 CSS 檔案加入網頁的 <head> 標記中:

      <script src="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.js"></script>
      <link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/6.0.1/firebase-ui-auth.css" />
      
    2. npm 模組

      使用下列指令,透過 npm 安裝 FirebaseUI 及其依附元件:

      $ npm install firebaseui --save
      

      require 來源檔案中的下列模組:

      var firebase = require('firebase');
      var firebaseui = require('firebaseui');
      
    3. 布林值元件

      使用下列指令,透過 Bower 安裝 FirebaseUI 及其依附元件:

      $ bower install firebaseui --save
      

      如果您的 HTTP 伺服器在 bower_components/ 內提供檔案,請在 HTML 中加入必要檔案:

      <script src="bower_components/firebaseui/dist/firebaseui.js"></script>
      <link type="text/css" rel="stylesheet" href="bower_components/firebaseui/dist/firebaseui.css" />
      

初始化 FirebaseUI

匯入 SDK 後,請初始化驗證 UI。

// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());

設定登入方式

您必須先啟用並設定您想支援的登入方式,才能使用 Firebase 登入使用者。

電子郵件地址和密碼

  1. Firebase 主控台中開啟「Authentication」區段,並啟用電子郵件和密碼驗證。

  2. 將電子郵件供應商 ID 新增至 FirebaseUI signInOptions 清單。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        firebase.auth.EmailAuthProvider.PROVIDER_ID
      ],
      // Other config options...
    });
    
  3. 選用:您可以設定 EmailAuthProvider,要求使用者輸入顯示名稱 (預設為 true)。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        {
          provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
          requireDisplayName: false
        }
      ]
    });
    
  1. Firebase 控制台開啟「驗證」專區。在「Sign in method」分頁中,啟用「Email/Password」供應商。請注意,您必須啟用電子郵件/密碼登入功能,才能使用電子郵件連結登入。

  2. 在相同區段中,啟用「Email link (無密碼登入)」登入方式,然後按一下「Save」(儲存)

  3. 將電子郵件服務供應商 ID 新增至 FirebaseUI signInOptions 清單,以及電子郵件連結 signInMethod

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        {
          provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
          signInMethod: firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD
        }
      ],
      // Other config options...
    });
    
  4. 有條件地轉譯登入 UI (與單一頁面應用程式相關) 時,請使用 ui.isPendingRedirect() 偵測網址是否對應於含有電子郵件連結的登入,且需要轉譯 UI 才能完成登入。

    // Is there an email link sign-in?
    if (ui.isPendingRedirect()) {
      ui.start('#firebaseui-auth-container', uiConfig);
    }
    // This can also be done via:
    if (firebase.auth().isSignInWithEmailLink(window.location.href)) {
      ui.start('#firebaseui-auth-container', uiConfig);
    }
    
  5. 選用:您可以設定電子郵件連結登入的 EmailAuthProvider,允許或禁止使用者完成跨裝置登入程序。

    您可以定義選用的 emailLinkSignIn 回呼,以傳回 firebase.auth.ActionCodeSettings 設定,在傳送連結時使用。這樣就能指定處理連結的方式、自訂動態連結、深層連結中的其他狀態等。如未提供,則會使用目前的網址,並觸發純網頁流程。

    FirebaseUI 網頁的電子郵件連結登入功能與 FirebaseUI-AndroidFirebaseUI-iOS 相容,讓從 FirebaseUI-Android 啟動流程的一位使用者可以開啟連結,並使用 FirebaseUI-web 完成登入。反向流程也是如此。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        {
          provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
          signInMethod: firebase.auth.EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD,
          // Allow the user the ability to complete sign-in cross device,
          // including the mobile apps specified in the ActionCodeSettings
          // object below.
          forceSameDevice: false,
          // Used to define the optional firebase.auth.ActionCodeSettings if
          // additional state needs to be passed along request and whether to open
          // the link in a mobile app if it is installed.
          emailLinkSignIn: function() {
            return {
              // Additional state showPromo=1234 can be retrieved from URL on
              // sign-in completion in signInSuccess callback by checking
              // window.location.href.
              url: 'https://www.example.com/completeSignIn?showPromo=1234',
              // Custom FDL domain.
              dynamicLinkDomain: 'example.page.link',
              // Always true for email link sign-in.
              handleCodeInApp: true,
              // Whether to handle link in iOS app if installed.
              iOS: {
                bundleId: 'com.example.ios'
              },
              // Whether to handle link in Android app if opened in an Android
              // device.
              android: {
                packageName: 'com.example.android',
                installApp: true,
                minimumVersion: '12'
              }
            };
          }
        }
      ]
    });
    

OAuth 提供者 (Google、Facebook、Twitter 和 GitHub)

  1. Firebase 主控台中開啟「Authentication」區段,並啟用指定的 OAuth 供應商登入。請確認也指定對應的 OAuth 用戶端 ID 和密鑰。

  2. 同樣在「Authentication」(驗證) 部分中,確保將顯示登入頁面的網域也加入授權網域清單。

  3. 將 OAuth 供應商 ID 新增至 FirebaseUI signInOptions 清單。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        // List of OAuth providers supported.
        firebase.auth.GoogleAuthProvider.PROVIDER_ID,
        firebase.auth.FacebookAuthProvider.PROVIDER_ID,
        firebase.auth.TwitterAuthProvider.PROVIDER_ID,
        firebase.auth.GithubAuthProvider.PROVIDER_ID
      ],
      // Other config options...
    });
    
  4. 選用:如要指定自訂範圍或每個供應商的自訂 OAuth 參數,您可以傳遞物件,而非只傳遞提供者值:

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        {
          provider: firebase.auth.GoogleAuthProvider.PROVIDER_ID,
          scopes: [
            'https://www.googleapis.com/auth/contacts.readonly'
          ],
          customParameters: {
            // Forces account selection even when one account
            // is available.
            prompt: 'select_account'
          }
        },
        {
          provider: firebase.auth.FacebookAuthProvider.PROVIDER_ID,
          scopes: [
            'public_profile',
            'email',
            'user_likes',
            'user_friends'
          ],
          customParameters: {
            // Forces password re-entry.
            auth_type: 'reauthenticate'
          }
        },
        firebase.auth.TwitterAuthProvider.PROVIDER_ID, // Twitter does not support scopes.
        firebase.auth.EmailAuthProvider.PROVIDER_ID // Other providers don't need to be given as object.
      ]
    });
    

電話號碼

  1. Firebase 控制台中開啟「驗證」專區,然後啟用電話號碼登入功能。

  2. 確保將顯示登入頁面的網域也新增至已授權的網域清單。

  3. 將電話號碼供應商 ID 加入 FirebaseUI signInOptions 清單中。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        firebase.auth.PhoneAuthProvider.PROVIDER_ID
      ],
      // Other config options...
    });
    
  4. 選用:您可以使用自訂 reCAPTCHA 參數設定 PhoneAuthProvider (預設為正常顯示 reCAPTCHA)。詳情請參閱 reCAPTCHA API 文件

    你也可以設定電話號碼輸入欄位的預設國家/地區。 如需完整的代碼清單,請參閱支援的國家/地區代碼清單。如未指定,電話號碼輸入將預設為美國 (+1)。

    以下是目前支援的選項。

    ui.start('#firebaseui-auth-container', {
      signInOptions: [
        {
          provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
          recaptchaParameters: {
            type: 'image', // 'audio'
            size: 'normal', // 'invisible' or 'compact'
            badge: 'bottomleft' //' bottomright' or 'inline' applies to invisible.
          },
          defaultCountry: 'GB', // Set default country to the United Kingdom (+44).
          // For prefilling the national number, set defaultNationNumber.
          // This will only be observed if only phone Auth provider is used since
          // for multiple providers, the NASCAR screen will always render first
          // with a 'sign in with phone number' button.
          defaultNationalNumber: '1234567890',
          // You can also pass the full phone number string instead of the
          // 'defaultCountry' and 'defaultNationalNumber'. However, in this case,
          // the first country ID that matches the country code will be used to
          // populate the country selector. So for countries that share the same
          // country code, the selected country may not be the expected one.
          // In that case, pass the 'defaultCountry' instead to ensure the exact
          // country is selected. The 'defaultCountry' and 'defaultNationaNumber'
          // will always have higher priority than 'loginHint' which will be ignored
          // in their favor. In this case, the default country will be 'GB' even
          // though 'loginHint' specified the country code as '+1'.
          loginHint: '+11234567890'
        }
      ]
    });
    

登入

如要啟動 FirebaseUI 登入流程,請傳遞基礎 Auth 執行個體來初始化 FirebaseUI 執行個體。

// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());

定義要轉譯 FirebaseUI 登入小工具的 HTML 元素。

<!-- The surrounding HTML is left untouched by FirebaseUI.
     Your app may use that space for branding, controls and other customizations.-->
<h1>Welcome to My Awesome App</h1>
<div id="firebaseui-auth-container"></div>
<div id="loader">Loading...</div>

指定 FirebaseUI 設定 (支援的供應商和 UI 自訂項目,以及成功回呼等)。

var uiConfig = {
  callbacks: {
    signInSuccessWithAuthResult: function(authResult, redirectUrl) {
      // User successfully signed in.
      // Return type determines whether we continue the redirect automatically
      // or whether we leave that to developer to handle.
      return true;
    },
    uiShown: function() {
      // The widget is rendered.
      // Hide the loader.
      document.getElementById('loader').style.display = 'none';
    }
  },
  // Will use popup for IDP Providers sign-in flow instead of the default, redirect.
  signInFlow: 'popup',
  signInSuccessUrl: '<url-to-redirect-to-on-success>',
  signInOptions: [
    // Leave the lines as is for the providers you want to offer your users.
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    firebase.auth.FacebookAuthProvider.PROVIDER_ID,
    firebase.auth.TwitterAuthProvider.PROVIDER_ID,
    firebase.auth.GithubAuthProvider.PROVIDER_ID,
    firebase.auth.EmailAuthProvider.PROVIDER_ID,
    firebase.auth.PhoneAuthProvider.PROVIDER_ID
  ],
  // Terms of service url.
  tosUrl: '<your-tos-url>',
  // Privacy policy url.
  privacyPolicyUrl: '<your-privacy-policy-url>'
};

最後,算繪 FirebaseUI 驗證介面:

// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);

升級匿名使用者

啟用匿名使用者升級

當匿名使用者以永久帳戶登入或註冊時,您一定會想確保使用者可以繼續完成註冊前進行的操作。方法是在設定登入 UI 時,將 autoUpgradeAnonymousUsers 設為 true (這個選項預設為停用)。

處理匿名使用者升級合併衝突

在某些情況下,使用者一開始以匿名方式登入,嘗試升級至現有的 Firebase 使用者。由於現有使用者無法連結至其他使用者,因此 FirebaseUI 會在發生上述情況時,觸發並傳回錯誤代碼 firebaseui/anonymous-upgrade-merge-conflictsignInFailure 回呼。錯誤物件也會包含永久憑證。使用永久憑證登入時,應該會在回呼中觸發以完成登入程序。您必須先儲存匿名使用者的資料並刪除匿名使用者,才能透過 auth.signInWithCredential(error.credential) 完成登入程序。登入完成後,將資料複製回非匿名使用者。以下範例說明此流程的運作方式。

// Temp variable to hold the anonymous user data if needed.
var data = null;
// Hold a reference to the anonymous current user.
var anonymousUser = firebase.auth().currentUser;
ui.start('#firebaseui-auth-container', {
  // Whether to upgrade anonymous users should be explicitly provided.
  // The user must already be signed in anonymously before FirebaseUI is
  // rendered.
  autoUpgradeAnonymousUsers: true,
  signInSuccessUrl: '<url-to-redirect-to-on-success>',
  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    firebase.auth.FacebookAuthProvider.PROVIDER_ID,
    firebase.auth.EmailAuthProvider.PROVIDER_ID,
    firebase.auth.PhoneAuthProvider.PROVIDER_ID
  ],
  callbacks: {
    // signInFailure callback must be provided to handle merge conflicts which
    // occur when an existing credential is linked to an anonymous user.
    signInFailure: function(error) {
      // For merge conflicts, the error.code will be
      // 'firebaseui/anonymous-upgrade-merge-conflict'.
      if (error.code != 'firebaseui/anonymous-upgrade-merge-conflict') {
        return Promise.resolve();
      }
      // The credential the user tried to sign in with.
      var cred = error.credential;
      // Copy data from anonymous user to permanent user and delete anonymous
      // user.
      // ...
      // Finish sign-in after data is copied.
      return firebase.auth().signInWithCredential(cred);
    }
  }
});

後續步驟

  • 如要進一步瞭解如何使用及自訂 FirebaseUI,請前往 README
  • 如果您發現 FirebaseUI 有問題並想回報,請使用 GitHub 問題追蹤工具