使用 JavaScript 透過電話號碼驗證 Firebase

您可以使用 Firebase 驗證功能,透過傳送簡訊到使用者的手機登入使用者。使用者使用簡訊中的一次性代碼登入。

如要在應用程式中加入電話號碼登入程序,最簡單的方法就是使用 FirebaseUI,其中包含一個用於實作電話號碼登入流程的置入登入小工具,以及密碼功能和聯合登入。本文說明如何使用 Firebase SDK 導入電話號碼登入流程。

事前準備

如果您尚未將初始化程式碼片段從 Firebase 控制台複製到專案,請按照「 將 Firebase 新增至 JavaScript 專案」一節所述。

安全疑慮

僅使用電話號碼進行驗證,但比其他可用的方法更安全,因為使用者可以在使用者之間輕鬆轉移電話號碼。此外,如果裝置上有多個使用者設定檔,任何可接收簡訊的使用者,都能使用裝置的電話號碼登入帳戶。

如果您在應用程式中使用以電話號碼為基礎的登入功能,建議您除了提供更安全的登入方式之外,也應向使用者說明使用電話號碼登入機制在安全性方面的保障。

為 Firebase 專案啟用電話號碼登入功能

如要透過簡訊登入使用者,您必須先為 Firebase 專案啟用電話號碼登入方式:

  1. Firebase 控制台開啟「驗證」專區。
  2. 在「Sign-in Method」(登入方式) 頁面中,啟用「Phone Number」(電話號碼) 登入方式。
  3. 在同一個頁面上,如果「OAuth 重新導向網域」部分未列出託管應用程式的網域,請加入您的網域。

Firebase 的電話號碼登入要求配額夠高,因此大多數應用程式不會受到影響。不過,如果您需要透過電話驗證來登入大量使用者,則可能需要升級定價方案。請參閱定價頁面。

設定 reCAPTCHA 驗證器

您必須先設定 Firebase 的 reCAPTCHA 驗證器,才能使用使用者的電話號碼登入。Firebase 會使用 reCAPTCHA 來防範濫用行為,例如確保電話號碼驗證要求來自應用程式允許的網域。

您不需要手動設定 reCAPTCHA 用戶端;當您使用 Firebase SDK 的 RecaptchaVerifier 物件時,Firebase 會自動建立和處理任何必要的用戶端金鑰和密鑰。

RecaptchaVerifier 物件支援隱形 reCAPTCHA 和 reCAPTCHA 小工具,這類功能通常不需要使用者操作即可驗證使用者,而且一律要求使用者互動才能完成。

在轉譯 reCAPTCHA 之前,您可以更新驗證執行個體上的語言代碼,依使用者偏好設定將基礎轉譯的 reCAPTCHA 本地化。上述本地化設定也適用於傳送給使用者的簡訊,其中包含驗證碼。

網頁模組 API

import { getAuth } from "firebase/auth";

const auth = getAuth();
auth.languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// auth.useDeviceLanguage();

網路命名空間 API

firebase.auth().languageCode = 'it';
// To apply the default browser preference instead of explicitly setting it.
// firebase.auth().useDeviceLanguage();

使用隱藏式 reCAPTCHA

如要使用隱藏的 reCAPTCHA,請建立 RecaptchaVerifier 物件,並將 size 參數設為 invisible,並指定用來提交登入表單的按鈕 ID。例如:

網頁模組 API

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

網路命名空間 API

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
  'size': 'invisible',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    onSignInSubmit();
  }
});

使用 reCAPTCHA 小工具

如要使用顯示的 reCAPTCHA 小工具,請在網頁中建立包含小工具的元素,然後建立 RecaptchaVerifier 物件,並在操作時指定容器 ID。例如:

網頁模組 API

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {});

網路命名空間 API

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');

選用:指定 reCAPTCHA 參數

您可以選擇在 RecaptchaVerifier 物件上設定回呼函式。系統會在使用者回答 reCAPTCHA 或 reCAPTCHA 之後,在使用者提交表單前呼叫回呼函式:

網頁模組 API

import { getAuth, RecaptchaVerifier } from "firebase/auth";

const auth = getAuth();
window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

網路命名空間 API

window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container', {
  'size': 'normal',
  'callback': (response) => {
    // reCAPTCHA solved, allow signInWithPhoneNumber.
    // ...
  },
  'expired-callback': () => {
    // Response expired. Ask user to solve reCAPTCHA again.
    // ...
  }
});

選用:預先轉譯 reCAPTCHA

如果要在提交登入要求前預先算繪 reCAPTCHA,請呼叫 render

網頁模組 API

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

網路命名空間 API

recaptchaVerifier.render().then((widgetId) => {
  window.recaptchaWidgetId = widgetId;
});

解析 render 後,您會取得 reCAPTCHA 小工具 ID,可用來呼叫 reCAPTCHA API:

網頁模組 API

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

網路命名空間 API

const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);

將驗證碼傳送至使用者的手機

如要啟動電話號碼登入程序,請向使用者顯示提示,提示他們提供電話號碼,然後呼叫 signInWithPhoneNumber 以要求 Firebase 透過簡訊將驗證碼傳送至使用者的手機:

  1. 取得使用者的電話號碼。

    相關法律要求各有不同,但您應告知使用者,讓他們使用手機登入時,可能會收到驗證簡訊和標準費率的簡訊。

  2. 呼叫 signInWithPhoneNumber,然後傳遞至使用者的電話號碼和您先前建立的 RecaptchaVerifier

    網頁模組 API

    import { getAuth, signInWithPhoneNumber } from "firebase/auth";
    
    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    
    const auth = getAuth();
    signInWithPhoneNumber(auth, phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });

    網路命名空間 API

    const phoneNumber = getPhoneNumberFromUserInput();
    const appVerifier = window.recaptchaVerifier;
    firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
        .then((confirmationResult) => {
          // SMS sent. Prompt user to type the code from the message, then sign the
          // user in with confirmationResult.confirm(code).
          window.confirmationResult = confirmationResult;
          // ...
        }).catch((error) => {
          // Error; SMS not sent
          // ...
        });
    如果 signInWithPhoneNumber 導致發生錯誤,請重設 reCAPTCHA,讓使用者能再試一次:
    grecaptcha.reset(window.recaptchaWidgetId);
    
    // Or, if you haven't stored the widget ID:
    window.recaptchaVerifier.render().then(function(widgetId) {
      grecaptcha.reset(widgetId);
    });
    

signInWithPhoneNumber 方法會向使用者發出 reCAPTCHA 驗證問題,如果使用者通過驗證,則要求 Firebase 驗證會將內含驗證碼的簡訊傳送至使用者的手機。

透過驗證碼登入使用者

呼叫 signInWithPhoneNumber 成功後,請提示使用者輸入簡訊收到的驗證碼。接著,將程式碼傳遞至傳遞至 signInWithPhoneNumber 執行要求處理常式 (即 then 區塊) 的 ConfirmationResult 物件的 confirm 方法,以便登入使用者。例如:

網頁模組 API

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

網路命名空間 API

const code = getCodeFromUserInput();
confirmationResult.confirm(code).then((result) => {
  // User signed in successfully.
  const user = result.user;
  // ...
}).catch((error) => {
  // User couldn't sign in (bad verification code?)
  // ...
});

如果呼叫 confirm 成功,使用者已成功登入。

取得中繼 AuthCredential 物件

如果您需要取得使用者帳戶的 AuthCredential 物件,請將確認結果中的驗證碼與驗證碼傳送至 PhoneAuthProvider.credential,不要呼叫 confirm

var credential = firebase.auth.PhoneAuthProvider.credential(confirmationResult.verificationId, code);

接著,您即可使用憑證登入使用者:

firebase.auth().signInWithCredential(credential);

使用虛構的電話號碼進行測試

您可以透過 Firebase 控制台設定用於開發的虛構電話號碼。使用虛構的電話號碼進行測試有以下優點:

  • 測試電話號碼驗證,而不耗用您的使用配額。
  • 在不傳送實際簡訊的情況下,測試電話號碼的驗證方式。
  • 以相同的電話號碼連續執行測試,而不會受到限制。這種做法可以盡可能降低應用程式商店審查流程中遭到拒絕的風險。這樣一來,如果審查人員當初是以同一組電話號碼進行測試,他們就會使用同一組電話號碼進行測試。
  • 無須額外費心,即可在開發環境中輕鬆進行測試,例如可以在 iOS 模擬器中進行開發,或是在沒有 Google Play 服務的 Android 模擬器中進行開發。
  • 編寫整合測試時,不會因實際電話號碼在正式環境中執行安全性檢查而遭到封鎖。

虛構電話號碼必須符合下列規定:

  1. 請務必使用虛構的電話號碼,而且尚未取得。 Firebase 驗證無法將真人使用者目前使用的電話號碼設為測試號碼。其中一種選項是使用 555 組的號碼做為美國測試電話號碼,例如:+1 650-555-3434
  2. 電話號碼必須符合長度和其他限制的正確格式。不過,他們仍須完成與實際使用者電話號碼相同的驗證。
  3. 您最多可以新增 10 組開發用電話號碼。
  4. 請使用難以猜到及變動的測試電話號碼/代碼。

建立虛構的電話號碼和驗證碼

  1. Firebase 控制台開啟「驗證」專區。
  2. 在「登入方式」分頁中啟用電話服務供應商 (如果您尚未啟用的話)。
  3. 開啟「測試用電話號碼」選單。
  4. 提供要測試的電話號碼,例如 +1 650-555-3434
  5. 提供該特定號碼的 6 位數驗證碼,例如「654321」
  6. 新增數字。如果需要,您可以刪除電話號碼及其程式碼,只要將滑鼠遊標懸停在對應資料列上,再按一下垃圾桶圖示即可。

手動測試

您可以直接開始在應用程式中使用虛構的電話號碼。這樣一來,您就能在開發階段執行手動測試,而不會遇到配額問題或節流問題。您也可以直接在未安裝 Google Play 服務的 iOS 模擬器或 Android 模擬器中進行測試。

如果提供虛構電話號碼並傳送驗證碼,系統不會傳送實際簡訊。您必須改為提供先前設定的驗證碼,才能完成登入程序。

登入完成後,系統會使用該電話號碼建立 Firebase 使用者。使用者和實際電話號碼使用者的行為和屬性相同,也能以相同方式存取即時資料庫/Cloud Firestore 和其他服務。在這個過程中,建立的 ID 權杖與實際電話號碼使用者的簽章相同。

如果想要進一步限制存取權,您也可以透過自訂聲明為這些使用者設定測試角色,藉此區分他們為假使用者。

整合測試

除了手動測試之外,Firebase 驗證也提供 API,協助您撰寫手機驗證測試的整合測試。這些 API 會停用網頁版 reCAPTCHA 要求,並在 iOS 中停用靜音推播通知,藉此停用應用程式驗證功能。如此一來,就能在這些流程中進行自動化測試,更輕鬆地實作。還可讓您測試 Android 上的即時驗證流程。

在網站上,請先將 appVerificationDisabledForTesting 設為 true,再轉譯 firebase.auth.RecaptchaVerifier。這樣系統會自動解析 reCAPTCHA,讓您可以傳遞電話號碼,而不必手動解決。請注意,即使已停用 reCAPTCHA,使用非虛構的電話號碼還是無法完成登入。這個 API 只能使用虛構的電話號碼。

// Turn off phone auth app verification.
firebase.auth().settings.appVerificationDisabledForTesting = true;

var phoneNumber = "+16505554567";
var testVerificationCode = "123456";

// This will render a fake reCAPTCHA as appVerificationDisabledForTesting is true.
// This will resolve after rendering without app verification.
var appVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
// signInWithPhoneNumber will call appVerifier.verify() which will resolve with a fake
// reCAPTCHA response.
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
    .then(function (confirmationResult) {
      // confirmationResult can resolve with the fictional testVerificationCode above.
      return confirmationResult.confirm(testVerificationCode)
    }).catch(function (error) {
      // Error; SMS not sent
      // ...
    });

停用應用程式驗證功能後,顯示和隱藏的模擬 reCAPTCHA 應用程式驗證器行為會有所不同:

  • 顯示的 reCAPTCHA:當可見的 reCAPTCHA 透過 appVerifier.render() 轉譯時,它會在一秒延遲後自動解析。這相當於使用者在轉譯後立即點選 reCAPTCHA。reCAPTCHA 回應會在一段時間後過期,之後自動重新自動解析。
  • 隱形 reCAPTCHA:隱形 reCAPTCHA 不會在轉譯時自動解析,而是在發生 appVerifier.verify() 呼叫或出現一秒後按下 reCAPTCHA 的按鈕錨點時。同樣地,回應會在一段時間後過期,且只會在 appVerifier.verify() 呼叫後或再次點選 reCAPTCHA 的按鈕錨點時自動解析。

每當模擬 reCAPTCHA 解析時,系統就會如預期地觸發包含假回應的回呼函式。如果一併指定到期回呼,則會在到期時觸發。

後續步驟

使用者首次登入時,系統會建立新的使用者帳戶,並連結至憑證 (即使用者名稱與密碼、電話號碼或驗證提供者資訊),也就是使用者登入時使用的憑證。這個新帳戶會儲存在您的 Firebase 專案中,可用來識別專案中各個應用程式的使用者 (無論使用者登入方式為何)。

  • 如要在應用程式中瞭解使用者的驗證狀態,建議在 Auth 物件上設定觀察器。接著,您就能從 User 物件取得使用者的基本個人資料資訊。請參閱管理使用者一文。

  • 在 Firebase 即時資料庫和 Cloud Storage 安全性規則中,您可以透過 auth 變數取得登入使用者的專屬 ID,並使用該 ID 控管使用者可存取哪些資料。

您可以將驗證供應商憑證連結至現有的使用者帳戶,讓使用者透過多個驗證服務提供者登入您的應用程式。

如要登出使用者,請呼叫 signOut

網頁模組 API

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

網路命名空間 API

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});