在網頁應用程式中加入 TOTP 多重驗證機制

如果您已升級至 Firebase Authentication with Identity Platform,可以為應用程式新增限時動態密碼 (TOTP) 多重驗證 (MFA)。

Firebase Authentication with Identity Platform 可讓您使用 TOTP 做為 MFA 的額外因素。啟用這項功能後,使用者嘗試登入應用程式時,就會看到 TOTP 的要求。如要產生這個驗證碼,客戶必須使用驗證器應用程式,才能產生有效的 TOTP 代碼,例如 Google Authenticator

事前準備

  1. 請啟用至少一個支援 MFA 的供應商。請注意,除了以下供應商以外,所有供應商都支援多重認證:

    • 電話驗證
    • 匿名驗證
    • 自訂驗證權杖
    • Apple Game Center
  2. 請確認您的應用程式會驗證使用者的電子郵件地址。多重驗證功能需要通過電子郵件驗證。這麼做可防止惡意人士使用不屬於自己的電子郵件地址註冊服務,然後透過新增第二個因素,將電子郵件地址的實際擁有者鎖在門外。

  3. 如果您尚未安裝 Firebase JavaScript SDK,請先安裝。

    模組化 Web SDK 的 TOTP MFA 僅支援 v9.19.1 以上版本。

啟用 TOTP MFA

如要啟用 TOTP 做為次要驗證方法,請使用 Admin SDK 或呼叫專案設定 REST 端點。

如要使用 Admin SDK,請執行下列操作:

  1. 如果您尚未安裝 Firebase Admin Node.js SDK,請先安裝。

    Firebase Admin Node.js SDK 11.6.0 以上版本僅支援 TOTP MFA。

  2. 執行以下指令:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: NUM_ADJ_INTERVALS
                  }
              }]
          }
    })
    

    更改下列內容:

    • NUM_ADJ_INTERVALS:接受 TOTP 的鄰近時間間隔數量,介於 0 到 10 之間。預設值為 5。

      TOTP 的運作方式是確保雙方 (驗證者和驗證者) 在相同的時間範圍內 (通常為 30 秒) 產生 OTP,產生的密碼都相同。不過,為因應各方之間的時間差異和人為回應時間,您可以將 TOTP 服務設為也接受相鄰視窗的 TOTP。

如要使用 REST API 啟用 TOTP MFA,請執行以下操作:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": NUM_ADJ_INTERVALS
            }
          }]
       }
    }'

更改下列內容:

  • PROJECT_ID:專案 ID。
  • NUM_ADJ_INTERVALS:時間區間間隔數量,從零到十。預設值為五。

    TOTP 的運作方式是確保雙方 (驗證者和驗證者) 在相同的時間範圍內 (通常為 30 秒) 產生 OTP,產生的密碼都相同。不過,為因應各方之間的時間差異和人為回應時間,您可以將 TOTP 服務設為也接受相鄰視窗的 TOTP。

選擇註冊模式

您可以選擇應用程式是否需要多重驗證,以及註冊使用者的方式和時間。常見的模式包括:

  • 在註冊過程中註冊使用者的第二重驗證。如果您的應用程式要求所有使用者都必須進行多重驗證,請使用這個方法。

  • 提供可略過的選項,讓使用者在註冊期間註冊第二個步驟。如果您想在應用程式中鼓勵使用多重驗證,但不強制要求使用,可以採用這種做法。

  • 提供從使用者帳戶或個人資料管理頁面新增第二個驗證因素的功能,而非在註冊畫面中提供。這樣一來,註冊程序的摩擦力就會降到最低,同時仍可讓重視安全性的使用者使用多重驗證功能。

  • 當使用者想要存取需要更高安全性要求的功能時,要求逐步新增第二個因素。

為使用者註冊 TOTP MFA

將 TOTP 多重驗證機制設為應用程式的第二個驗證機制後,請實作用戶端邏輯,讓使用者註冊 TOTP 多重驗證機制:

  1. 匯入必要的 MFA 類別和函式:

    import {
      multiFactor,
      TotpMultiFactorGenerator,
      TotpSecret,
      getAuth,
    } from "firebase/auth";
    
  2. 重新驗證使用者。

  3. 為已驗證的使用者產生 TOTP 密鑰:

    // Generate a TOTP secret.
    const multiFactorSession = await multiFactor(currentUser).getSession();
    const totpSecret = await TotpMultiFactorGenerator.generateSecret(
      multiFactorSession
    );
    
  4. 向使用者顯示密鑰,並提示他們在驗證器應用程式中輸入密鑰。

    許多 authenticator 應用程式都支援掃描代表 Google Authenticator 相容金鑰 URI 的 QR code,讓使用者可以快速新增 TOTP 密鑰。如要為此目的產生 QR code,請使用 generateQrCodeUrl() 產生 URI,然後使用所選的 QR code 程式庫進行編碼。例如:

    const totpUri = totpSecret.generateQrCodeUrl(
        currentUser.email,
        "Your App's Name"
    );
    await QRExampleLib.toCanvas(totpUri, qrElement);
    

    無論您是否顯示 QR code,請一律顯示密鑰,以支援無法讀取 QR code 的驗證器應用程式:

    // Also display this key:
    const secret = totpSecret.secretKey;
    

    使用者將密鑰新增至驗證器應用程式後,就會開始產生 TOTP。

  5. 提示使用者輸入驗證器應用程式顯示的 TOTP,並使用該值完成多因素驗證註冊程序:

    // Ask the user for a verification code from the authenticator app.
    const verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    const multiFactorAssertion = TotpMultiFactorGenerator.assertionForEnrollment(
      totpSecret,
      verificationCode
    );
    await multiFactor(currentUser).enroll(multiFactorAssertion, mfaDisplayName);
    

使用雙重驗證登入使用者

如要透過 TOTP MFA 登入使用者,請使用以下代碼:

  1. 匯入必要的 MFA 類別和函式:

    import {
        getAuth,
        getMultiFactorResolver,
        TotpMultiFactorGenerator,
    } from "firebase/auth";
    
  2. 如同未使用多重身份驗證 (MFA) 一樣,呼叫其中一個 signInWith 方法。(例如 signInWithEmailAndPassword()) 如果方法擲回 auth/multi-factor-auth-required 錯誤,請啟動應用程式的多重身分驗證流程。

    try {
        const userCredential = await signInWithEmailAndPassword(
            getAuth(),
            email,
            password
        );
        // If the user is not enrolled with a second factor and provided valid
        // credentials, sign-in succeeds.
    
        // (If your app requires MFA, this could be considered an error
        // condition, which you would resolve by forcing the user to enroll a
        // second factor.)
    
        // ...
    } catch (error) {
        switch (error.code) {
            case "auth/multi-factor-auth-required":
                // Initiate your second factor sign-in flow. (See next step.)
                // ...
                break;
            case ...:  // Handle other errors, such as wrong passwords.
                break;
        }
    }
    
  3. 應用程式的 MFA 流程應先提示使用者選擇要使用的第二個因素。您可以檢查 MultiFactorResolver 執行個體的 hints 屬性,取得支援的第二因子清單:

    const mfaResolver = getMultiFactorResolver(getAuth(), error);
    const enrolledFactors = mfaResolver.hints.map(info => info.displayName);
    
  4. 如果使用者選擇使用 TOTP,請提示他們輸入 Authenticator 應用程式顯示的 TOTP,並使用該代碼登入:

    switch (mfaResolver.hints[selectedIndex].factorId) {
        case TotpMultiFactorGenerator.FACTOR_ID:
            const otpFromAuthenticator = // OTP typed by the user.
            const multiFactorAssertion =
                TotpMultiFactorGenerator.assertionForSignIn(
                    mfaResolver.hints[selectedIndex].uid,
                    otpFromAuthenticator
                );
            try {
                const userCredential = await mfaResolver.resolveSignIn(
                    multiFactorAssertion
                );
                // Successfully signed in!
            } catch (error) {
                // Invalid or expired OTP.
            }
            break;
        case PhoneMultiFactorGenerator.FACTOR_ID:
            // Handle SMS second factor.
            break;
        default:
            // Unsupported second factor?
            break;
    }
    

取消註冊 TOTP 多重驗證

本節說明如何處理使用者取消註冊 TOTP MFA 的情況。

如果使用者已註冊多個多重驗證選項,且從最近啟用的選項取消註冊,系統會傳送 auth/user-token-expired 並登出使用者。使用者必須重新登入並驗證現有的憑證,例如電子郵件地址和密碼。

如要取消註冊使用者、處理錯誤,並觸發重新驗證程序,請使用下列程式碼:

import {
    EmailAuthProvider,
    TotpMultiFactorGenerator,
    getAuth,
    multiFactor,
    reauthenticateWithCredential,
} from "firebase/auth";

try {
    // Unenroll from TOTP MFA.
    await multiFactor(currentUser).unenroll(mfaEnrollmentId);
} catch  (error) {
    if (error.code === 'auth/user-token-expired') {
        // If the user was signed out, re-authenticate them.

        // For example, if they signed in with a password, prompt them to
        // provide it again, then call `reauthenticateWithCredential()` as shown
        // below.

        const credential = EmailAuthProvider.credential(email, password);
        await reauthenticateWithCredential(
            currentUser,
            credential
        );
    }
}

後續步驟