如果您已升級至 Firebase Authentication with Identity Platform,即可新增簡訊多重驗證 為您的網頁應用程式
多重驗證可提高應用程式安全性。雖然攻擊者 經常會竊取密碼和社群媒體帳戶、攔截簡訊, 變得更困難
事前準備
- 請至少啟用一個支援多重驗證的供應商。 每個供應商都支援多重驗證,但電話驗證、匿名驗證和 Apple Game Center。 
- 請啟用您要使用簡訊驗證功能的區域。 「Firebase」使用完全封鎖簡訊區域政策。 預設會以較安全的狀態建立專案 
- 確認應用程式正在驗證使用者的電子郵件地址。MFA 需要電子郵件驗證。 這可防止惡意人士利用電子郵件註冊服務 成為非擁有人,進而鎖定真正的擁有者 。 
使用多用戶群架構
如果您啟用多重驗證機制 多用戶群環境,請確定 除了 請按照以下說明操作):
- 在 Google Cloud 控制台中,選取要使用的用戶群。 
- 在程式碼中,將 - Auth例項上的- tenantId欄位設為 用戶群 ID。例如:- Web- import { getAuth } from "firebase/auth"; const auth = getAuth(app); auth.tenantId = "myTenantId1";- Web- firebase.auth().tenantId = 'myTenantId1';
啟用多重驗證
- 開啟「驗證 >登入方式 Firebase控制台中的頁面。 
- 在「進階」部分中,啟用「簡訊多重驗證」。 - 請一併輸入要測試應用程式的電話號碼。 雖然非必要,但我們強烈建議您註冊測試電話號碼 避免開發過程中發生節流現象 
- 如果您尚未授權應用程式的網域,請將網域新增至允許清單 [驗證] > 中的清單設定 Firebase控制台中的頁面。 
選擇註冊模式
您可以選擇是否要讓應用程式需要多因素驗證,以及如何 以及註冊使用者的時間常見的模式包括:
- 註冊使用者的第二重驗證在註冊過程中。使用這份草稿 方法。 
- 提供可略過的選項,讓使用者在註冊期間註冊第二個步驟。應用程式 想鼓勵但不要求進行多因素驗證時 他們偏好使用這個方法。 
- 讓使用者能夠在帳戶或設定檔中新增次要驗證條件 而非註冊畫面這可以大幅減少 同時在進行多因素驗證時 。 
- 使用者要存取應用程式時,需要逐步新增第二個驗證步驟 強化安全性要求的功能 
設定 reCAPTCHA 驗證器
您必須先設定 reCAPTCHA 驗證器,才能傳送簡訊驗證碼。 Firebase 使用 reCAPTCHA 防堵濫用行為, 電話號碼驗證要求來自應用程式允許的網域。
您不需要手動設定 reCAPTCHA 用戶端。用戶端 SDK 的
RecaptchaVerifier 物件會自動建立並初始化任何必要物件
用戶端金鑰和密鑰。
使用隱形 reCAPTCHA
RecaptchaVerifier 物件支援
隱形 reCAPTCHA、
通常可以在不需要任何互動的情況下驗證使用者如要使用
隱藏式 reCAPTCHA,建立含有 size 參數的 RecaptchaVerifier
為 invisible,並指定啟動多重要素的 UI 元素 ID
註冊:
Web
import { RecaptchaVerifier, getAuth } from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier(getAuth(), "sign-in-button", {
    "size": "invisible",
    "callback": function(response) {
        // reCAPTCHA solved, you can proceed with
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
    }
});
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
  // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
  onSolvedRecaptcha();
}
});
使用 reCAPTCHA 小工具
如要使用可見的 reCAPTCHA 小工具,請建立 HTML 元素以納入
,然後使用 UI 的 ID 建立 RecaptchaVerifier 物件。
都會在 Docker 容器中執行您也可以選擇設定在
reCAPTCHA 已解決或過期:
Web
import { RecaptchaVerifier, getAuth } from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier(
    getAuth(),
    "recaptcha-container",
    // Optional reCAPTCHA parameters.
    {
      "size": "normal",
      "callback": function(response) {
        // reCAPTCHA solved, you can proceed with
        // phoneAuthProvider.verifyPhoneNumber(...).
        onSolvedRecaptcha();
      },
      "expired-callback": function() {
        // Response expired. Ask user to solve reCAPTCHA again.
        // ...
      }
    }
);
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
  'recaptcha-container',
  // Optional reCAPTCHA parameters.
  {
    'size': 'normal',
    'callback': function(response) {
      // reCAPTCHA solved, you can proceed with phoneAuthProvider.verifyPhoneNumber(...).
      // ...
      onSolvedRecaptcha();
    },
    'expired-callback': function() {
      // Response expired. Ask user to solve reCAPTCHA again.
      // ...
    }
  });
預先轉譯 reCAPTCHA
您可以視需要在啟動雙重驗證前預先轉譯 reCAPTCHA 註冊:
Web
recaptchaVerifier.render()
    .then(function (widgetId) {
        window.recaptchaWidgetId = widgetId;
    });
Web
recaptchaVerifier.render()
  .then(function(widgetId) {
    window.recaptchaWidgetId = widgetId;
  });
render() 解析後,您會收到 reCAPTCHA 小工具 ID,您可以使用該 ID
呼叫
reCAPTCHA API:
var recaptchaResponse = grecaptcha.getResponse(window.recaptchaWidgetId);
reCAPTCHA 可透過 verify 方法提取這個邏輯,因此您不需要直接處理 grecaptcha 變數。
註冊次要驗證方式
如何為使用者註冊新的次要驗證條件:
- 重新驗證使用者。 
- 請使用者輸入電話號碼。 
- 請初始化 reCAPTCHA 驗證器,如上一節所示。 如果已設定 RecaptchaVerifier 執行個體,請略過這個步驟: - Web- import { RecaptchaVerifier, getAuth } from "firebase/auth"; const recaptchaVerifier = new RecaptchaVerifier( getAuth(),'recaptcha-container-id', undefined);- Web- var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
- 為使用者取得多重要素工作階段: - Web- import { multiFactor } from "firebase/auth"; multiFactor(user).getSession().then(function (multiFactorSession) { // ... });- Web- user.multiFactor.getSession().then(function(multiFactorSession) { // ... })
- 使用使用者的電話號碼和 - PhoneInfoOptions多因素工作階段:- Web- // Specify the phone number and pass the MFA session. const phoneInfoOptions = { phoneNumber: phoneNumber, session: multiFactorSession };- Web- // Specify the phone number and pass the MFA session. var phoneInfoOptions = { phoneNumber: phoneNumber, session: multiFactorSession };
- 傳送驗證訊息到使用者的手機: - Web- import { PhoneAuthProvider } from "firebase/auth"; const phoneAuthProvider = new PhoneAuthProvider(auth); phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function (verificationId) { // verificationId will be needed to complete enrollment. });- Web- var phoneAuthProvider = new firebase.auth.PhoneAuthProvider(); // Send SMS verification code. return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function(verificationId) { // verificationId will be needed for enrollment completion. })- 雖然並非必要,但最佳做法是事先告知使用者 會收到簡訊,支付一般簡訊費用 
- 如果要求失敗,請重設 reCAPTCHA,然後重複上一個步驟 讓使用者再試一次。請注意, - verifyPhoneNumber()會 會在擲回錯誤時自動重設 reCAPTCHA,例如 reCAPTCHA 權杖只能使用一次。- Web- recaptchaVerifier.clear();- Web- recaptchaVerifier.clear();
- 讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼: - Web- // Ask user for the verification code. Then: const cred = PhoneAuthProvider.credential(verificationId, verificationCode);- Web- // Ask user for the verification code. Then: var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
- 使用 - PhoneAuthCredential初始化- MultiFactorAssertion物件:- Web- import { PhoneMultiFactorGenerator } from "firebase/auth"; const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);- Web- var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
- 完成註冊程序。您也可以選擇為 第二個步驟這項功能適用於具有多項第二項因素的使用者 系統會在驗證流程中遮蓋電話號碼 (例如, 例如 +1******1234)。 - Web- // Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. multiFactor(user).enroll(multiFactorAssertion, "My personal phone number");- Web- // Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. user.multiFactor.enroll(multiFactorAssertion, 'My personal phone number');
以下程式碼顯示第二重驗證註冊的完整範例:
Web
import {
    multiFactor, PhoneAuthProvider, PhoneMultiFactorGenerator,
    RecaptchaVerifier, getAuth
} from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier(getAuth(),
    'recaptcha-container-id', undefined);
multiFactor(user).getSession()
    .then(function (multiFactorSession) {
        // Specify the phone number and pass the MFA session.
        const phoneInfoOptions = {
            phoneNumber: phoneNumber,
            session: multiFactorSession
        };
        const phoneAuthProvider = new PhoneAuthProvider(auth);
        // Send SMS verification code.
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier);
    }).then(function (verificationId) {
        // Ask user for the verification code. Then:
        const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
        const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
        // Complete enrollment.
        return multiFactor(user).enroll(multiFactorAssertion, mfaDisplayName);
    });
Web
var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
user.multiFactor.getSession().then(function(multiFactorSession) {
  // Specify the phone number and pass the MFA session.
  var phoneInfoOptions = {
    phoneNumber: phoneNumber,
    session: multiFactorSession
  };
  var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
  // Send SMS verification code.
  return phoneAuthProvider.verifyPhoneNumber(
      phoneInfoOptions, recaptchaVerifier);
})
.then(function(verificationId) {
  // Ask user for the verification code.
  var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
  var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
  // Complete enrollment.
  return user.multiFactor.enroll(multiFactorAssertion, mfaDisplayName);
});
恭喜!您已成功註冊以下項目的第二個驗證方式: 而非個別使用者的帳戶
透過次要驗證方式登入使用者
如何透過雙重簡訊驗證功能登入使用者帳戶:
- 透過第一個步驟登入使用者,然後擷取 - auth/multi-factor-auth-required個錯誤。這項錯誤含有 解析器、註冊第二要素的提示,以及基礎工作階段 證明使用者已透過第一個因素成功通過驗證。- 舉例來說,如果使用者的第一個驗證方法是電子郵件地址和密碼: - Web- import { getAuth, signInWithEmailAndPassword, getMultiFactorResolver} from "firebase/auth"; const auth = getAuth(); signInWithEmailAndPassword(auth, email, password) .then(function (userCredential) { // User successfully signed in and is not enrolled with a second factor. }) .catch(function (error) { if (error.code == 'auth/multi-factor-auth-required') { // The user is a multi-factor user. Second factor challenge is required. resolver = getMultiFactorResolver(auth, error); // ... } else if (error.code == 'auth/wrong-password') { // Handle other errors such as wrong password. } });- Web- firebase.auth().signInWithEmailAndPassword(email, password) .then(function(userCredential) { // User successfully signed in and is not enrolled with a second factor. }) .catch(function(error) { if (error.code == 'auth/multi-factor-auth-required') { // The user is a multi-factor user. Second factor challenge is required. resolver = error.resolver; // ... } else if (error.code == 'auth/wrong-password') { // Handle other errors such as wrong password. } ... });- 如果使用者的第一個因素是聯合提供者,例如 OAuth、SAML 或 OIDC,呼叫 - signInWithPopup()後擷取錯誤,或- signInWithRedirect()。
- 如果使用者已註冊多項次要驗證因素,請詢問他們哪個 使用: - Web- // Ask user which second factor to use. // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber // You can get the display name via resolver.hints[selectedIndex].displayName if (resolver.hints[selectedIndex].factorId === PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. // ... } else if (resolver.hints[selectedIndex].factorId === TotpMultiFactorGenerator.FACTOR_ID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }- Web- // Ask user which second factor to use. // You can get the masked phone number via resolver.hints[selectedIndex].phoneNumber // You can get the display name via resolver.hints[selectedIndex].displayName if (resolver.hints[selectedIndex].factorId === firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. // ... } else if (resolver.hints[selectedIndex].factorId === firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) { // User selected a TOTP second factor. // ... } else { // Unsupported second factor. }
- 請初始化 reCAPTCHA 驗證器,如上一節所示。 如果已設定 RecaptchaVerifier 執行個體,請略過這個步驟: - Web- import { RecaptchaVerifier, getAuth } from "firebase/auth"; recaptchaVerifier = new RecaptchaVerifier(getAuth(), 'recaptcha-container-id', undefined);- Web- var recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container-id');
- 使用使用者的電話號碼和 - PhoneInfoOptions多因素工作階段這些值包含在- resolver中 傳遞至- auth/multi-factor-auth-required錯誤的物件:- Web- const phoneInfoOptions = { multiFactorHint: resolver.hints[selectedIndex], session: resolver.session };- Web- var phoneInfoOptions = { multiFactorHint: resolver.hints[selectedIndex], session: resolver.session };
- 傳送驗證訊息到使用者的手機: - Web- // Send SMS verification code. const phoneAuthProvider = new PhoneAuthProvider(auth); phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function (verificationId) { // verificationId will be needed for sign-in completion. });- Web- var phoneAuthProvider = new firebase.auth.PhoneAuthProvider(); // Send SMS verification code. return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier) .then(function(verificationId) { // verificationId will be needed for sign-in completion. })
- 如果要求失敗,請重設 reCAPTCHA,然後重複上一個步驟 以便使用者再試一次: - Web- recaptchaVerifier.clear();- Web- recaptchaVerifier.clear();
- 讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼: - Web- const cred = PhoneAuthProvider.credential(verificationId, verificationCode);- Web- // Ask user for the verification code. Then: var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode);
- 使用 - PhoneAuthCredential初始化- MultiFactorAssertion物件:- Web- const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);- Web- var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
- 請呼叫 - resolver.resolveSignIn()完成次要驗證。 接著,您可以存取原始登入結果,包括 標準供應商專屬資料和驗證憑證:- Web- // Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(multiFactorAssertion) .then(function (userCredential) { // userCredential will also contain the user, additionalUserInfo, optional // credential (null for email/password) associated with the first factor sign-in. // For example, if the user signed in with Google as a first factor, // userCredential.additionalUserInfo will contain data related to Google // provider that the user signed in with. // - user.credential contains the Google OAuth credential. // - user.credential.accessToken contains the Google OAuth access token. // - user.credential.idToken contains the Google OAuth ID token. });- Web- // Complete sign-in. This will also trigger the Auth state listeners. resolver.resolveSignIn(multiFactorAssertion) .then(function(userCredential) { // userCredential will also contain the user, additionalUserInfo, optional // credential (null for email/password) associated with the first factor sign-in. // For example, if the user signed in with Google as a first factor, // userCredential.additionalUserInfo will contain data related to Google provider that // the user signed in with. // user.credential contains the Google OAuth credential. // user.credential.accessToken contains the Google OAuth access token. // user.credential.idToken contains the Google OAuth ID token. });
以下程式碼是多因素使用者登入的完整範例:
Web
import {
    getAuth,
    getMultiFactorResolver,
    PhoneAuthProvider,
    PhoneMultiFactorGenerator,
    RecaptchaVerifier,
    signInWithEmailAndPassword
} from "firebase/auth";
const recaptchaVerifier = new RecaptchaVerifier(getAuth(),
    'recaptcha-container-id', undefined);
const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
    .then(function (userCredential) {
        // User is not enrolled with a second factor and is successfully
        // signed in.
        // ...
    })
    .catch(function (error) {
        if (error.code == 'auth/multi-factor-auth-required') {
            const resolver = getMultiFactorResolver(auth, error);
            // Ask user which second factor to use.
            if (resolver.hints[selectedIndex].factorId ===
                PhoneMultiFactorGenerator.FACTOR_ID) {
                const phoneInfoOptions = {
                    multiFactorHint: resolver.hints[selectedIndex],
                    session: resolver.session
                };
                const phoneAuthProvider = new PhoneAuthProvider(auth);
                // Send SMS verification code
                return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
                    .then(function (verificationId) {
                        // Ask user for the SMS verification code. Then:
                        const cred = PhoneAuthProvider.credential(
                            verificationId, verificationCode);
                        const multiFactorAssertion =
                            PhoneMultiFactorGenerator.assertion(cred);
                        // Complete sign-in.
                        return resolver.resolveSignIn(multiFactorAssertion)
                    })
                    .then(function (userCredential) {
                        // User successfully signed in with the second factor phone number.
                    });
            } else if (resolver.hints[selectedIndex].factorId ===
                       TotpMultiFactorGenerator.FACTOR_ID) {
                // Handle TOTP MFA.
                // ...
            } else {
                // Unsupported second factor.
            }
        } else if (error.code == 'auth/wrong-password') {
            // Handle other errors such as wrong password.
        }
    });
Web
var resolver;
firebase.auth().signInWithEmailAndPassword(email, password)
  .then(function(userCredential) {
    // User is not enrolled with a second factor and is successfully signed in.
    // ...
  })
  .catch(function(error) {
    if (error.code == 'auth/multi-factor-auth-required') {
      resolver = error.resolver;
      // Ask user which second factor to use.
      if (resolver.hints[selectedIndex].factorId ===
          firebase.auth.PhoneMultiFactorGenerator.FACTOR_ID) {
        var phoneInfoOptions = {
          multiFactorHint: resolver.hints[selectedIndex],
          session: resolver.session
        };
        var phoneAuthProvider = new firebase.auth.PhoneAuthProvider();
        // Send SMS verification code
        return phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
          .then(function(verificationId) {
            // Ask user for the SMS verification code.
            var cred = firebase.auth.PhoneAuthProvider.credential(
                verificationId, verificationCode);
            var multiFactorAssertion =
                firebase.auth.PhoneMultiFactorGenerator.assertion(cred);
            // Complete sign-in.
            return resolver.resolveSignIn(multiFactorAssertion)
          })
          .then(function(userCredential) {
            // User successfully signed in with the second factor phone number.
          });
      } else if (resolver.hints[selectedIndex].factorId ===
        firebase.auth.TotpMultiFactorGenerator.FACTOR_ID) {
        // Handle TOTP MFA.
        // ...
      } else {
        // Unsupported second factor.
      }
    } else if (error.code == 'auth/wrong-password') {
      // Handle other errors such as wrong password.
    } ...
  });
恭喜!您已成功透過多重要素登入使用者 驗證。
後續步驟
- 管理多重驗證使用者 透過程式利用 Admin SDK。