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

如果您已升級至 Firebase Authentication with Identity Platform,即可新增簡訊多重驗證 為您的網頁應用程式

多重驗證可提高應用程式安全性。雖然攻擊者 經常會竊取密碼和社群媒體帳戶、攔截簡訊, 變得更困難

事前準備

  1. 請至少啟用一個支援多重驗證的供應商。 每個供應商都支援多重驗證,但電話驗證、匿名驗證和 Apple Game Center。

  2. 請啟用您要使用簡訊驗證功能的區域。 「Firebase」使用完全封鎖簡訊區域政策。 預設會以較安全的狀態建立專案

  3. 確認應用程式正在驗證使用者的電子郵件地址。MFA 需要電子郵件驗證。 這可防止惡意人士利用電子郵件註冊服務 成為非擁有人,進而鎖定真正的擁有者 。

使用多用戶群架構

如果您啟用多重驗證機制 多用戶群環境,請確定 除了 請按照以下說明操作):

  1. 在 Google Cloud 控制台中,選取要使用的用戶群。

  2. 在程式碼中,將 Auth 例項上的 tenantId 欄位設為 用戶群 ID。例如:

    Web

    import { getAuth } from "firebase/auth";  const auth = getAuth(app); auth.tenantId = "myTenantId1"; 

    Web

    firebase.auth().tenantId = 'myTenantId1'; 

啟用多重驗證

  1. 開啟「驗證 >登入方式 Firebase控制台中的頁面。

  2. 在「進階」部分中,啟用「簡訊多重驗證」

    請一併輸入要測試應用程式的電話號碼。 雖然非必要,但我們強烈建議您註冊測試電話號碼 避免開發過程中發生節流現象

  3. 如果您尚未授權應用程式的網域,請將網域新增至允許清單 [驗證] > 中的清單設定 Firebase控制台中的頁面。

選擇註冊模式

您可以選擇是否要讓應用程式需要多因素驗證,以及如何 以及註冊使用者的時間常見的模式包括:

  • 註冊使用者的第二重驗證在註冊過程中。使用這份草稿 方法。

  • 提供可略過的選項,讓使用者在註冊期間註冊第二個步驟。應用程式 想鼓勵但不要求進行多因素驗證時 他們偏好使用這個方法。

  • 讓使用者能夠在帳戶或設定檔中新增次要驗證條件 而非註冊畫面這可以大幅減少 同時在進行多因素驗證時 。

  • 使用者要存取應用程式時,需要逐步新增第二個驗證步驟 強化安全性要求的功能

設定 reCAPTCHA 驗證器

您必須先設定 reCAPTCHA 驗證器,才能傳送簡訊驗證碼。 Firebase 使用 reCAPTCHA 防堵濫用行為, 電話號碼驗證要求來自應用程式允許的網域。

您不需要手動設定 reCAPTCHA 用戶端。用戶端 SDK 的 RecaptchaVerifier 物件會自動建立並初始化任何必要物件 用戶端金鑰和密鑰。

使用隱形 reCAPTCHA

RecaptchaVerifier 物件支援 隱形 reCAPTCHA、 通常可以在不需要任何互動的情況下驗證使用者如要使用 隱藏式 reCAPTCHA,建立含有 size 參數的 RecaptchaVerifierinvisible,並指定啟動多重要素的 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 變數。

註冊次要驗證方式

如何為使用者註冊新的次要驗證條件:

  1. 重新驗證使用者。

  2. 請使用者輸入電話號碼。

  3. 請初始化 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'); 
  4. 為使用者取得多重要素工作階段:

    Web

    import { multiFactor } from "firebase/auth";  multiFactor(user).getSession().then(function (multiFactorSession) {     // ... }); 

    Web

    user.multiFactor.getSession().then(function(multiFactorSession) {   // ... }) 
  5. 使用使用者的電話號碼和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 }; 
  6. 傳送驗證訊息到使用者的手機:

    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.   }) 

    雖然並非必要,但最佳做法是事先告知使用者 會收到簡訊,支付一般簡訊費用

  7. 如果要求失敗,請重設 reCAPTCHA,然後重複上一個步驟 讓使用者再試一次。請注意,verifyPhoneNumber() 會 會在擲回錯誤時自動重設 reCAPTCHA,例如 reCAPTCHA 權杖只能使用一次。

    Web

    recaptchaVerifier.clear(); 

    Web

    recaptchaVerifier.clear(); 
  8. 讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼:

    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); 
  9. 使用 PhoneAuthCredential 初始化 MultiFactorAssertion 物件:

    Web

    import { PhoneMultiFactorGenerator } from "firebase/auth";  const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred); 

    Web

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred); 
  10. 完成註冊程序。您也可以選擇為 第二個步驟這項功能適用於具有多項第二項因素的使用者 系統會在驗證流程中遮蓋電話號碼 (例如, 例如 +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); }); 

恭喜!您已成功註冊以下項目的第二個驗證方式: 而非個別使用者的帳戶

透過次要驗證方式登入使用者

如何透過雙重簡訊驗證功能登入使用者帳戶:

  1. 透過第一個步驟登入使用者,然後擷取 auth/multi-factor-auth-required 個錯誤。這項錯誤含有 解析器、註冊第二要素的提示,以及基礎工作階段 證明使用者已透過第一個因素成功通過驗證。

    舉例來說,如果使用者的第一個驗證方法是電子郵件地址和密碼:

    Web

    import { getAuth, 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()

  2. 如果使用者已註冊多項次要驗證因素,請詢問他們哪個 使用:

    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. } 
  3. 請初始化 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'); 
  4. 使用使用者的電話號碼和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 }; 
  5. 傳送驗證訊息到使用者的手機:

    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.   }) 
  6. 如果要求失敗,請重設 reCAPTCHA,然後重複上一個步驟 以便使用者再試一次:

    Web

    recaptchaVerifier.clear(); 

    Web

    recaptchaVerifier.clear(); 
  7. 讓系統傳送簡訊驗證碼後,要求使用者驗證驗證碼:

    Web

    const cred = PhoneAuthProvider.credential(verificationId, verificationCode); 

    Web

    // Ask user for the verification code. Then: var cred = firebase.auth.PhoneAuthProvider.credential(verificationId, verificationCode); 
  8. 使用 PhoneAuthCredential 初始化 MultiFactorAssertion 物件:

    Web

    const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred); 

    Web

    var multiFactorAssertion = firebase.auth.PhoneMultiFactorGenerator.assertion(cred); 
  9. 請呼叫 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.     } ...   }); 

恭喜!您已成功透過多重要素登入使用者 驗證。

後續步驟