您可以透過 Firebase Authentication 將簡訊傳送至使用者手機,讓使用者登入。使用者透過簡訊中的一次性代碼登入。
如要輕鬆在應用程式中新增電話號碼登入功能,最簡單的方法是使用 FirebaseUI,其中包含置入式登入小工具,可實作電話號碼登入、密碼登入和聯合登入的登入流程。本文說明如何使用 Firebase SDK 導入手機號碼登入流程。
事前準備
如果尚未複製,請從 Firebase 控制台將初始化程式碼片段複製到專案,如「 將 Firebase 新增至 JavaScript 專案」一文所述。安全疑慮
僅使用電話號碼驗證雖然方便,但安全性不如其他可用方法,因為電話號碼很容易在使用者之間轉移。此外,在有多個使用者設定檔的裝置上,只要使用者可以接收簡訊,就能使用裝置的電話號碼登入帳戶。
如果應用程式採用電話號碼登入方式,請一併提供更安全的登入方式,並告知使用者使用電話號碼登入的安全性考量。
為 Firebase 專案啟用電話號碼登入功能
如要透過簡訊登入使用者,請先為 Firebase 專案啟用電話號碼登入方法:
- 在 Firebase 控制台中,開啟「驗證」部分。
- 在「登入方式」頁面中,啟用「電話號碼」登入方式。
- 在「設定」頁面中,針對要允許或拒絕傳送簡訊的區域設定政策。對於新專案,預設政策不允許任何區域。
- 在同一頁面中,如果應用程式代管網域未列在「OAuth 重新導向網域」部分,請新增網域。請注意,為了進行電話號碼驗證,系統不允許使用本機主機做為代管網域。
設定 reCAPTCHA 驗證器
您必須先設定 Firebase 的 reCAPTCHA 驗證器,才能讓使用者透過電話號碼登入。Firebase 會使用 reCAPTCHA 防止濫用行為,例如確保電話號碼驗證要求來自應用程式的允許網域。
您不需要手動設定 reCAPTCHA 用戶端,使用 Firebase SDK 的 RecaptchaVerifier 物件時,Firebase 會自動建立及處理所有必要的用戶端金鑰和密鑰。
RecaptchaVerifier 物件支援隱形 reCAPTCHA,通常不需要使用者採取任何動作即可驗證身分,也支援 reCAPTCHA 小工具,但使用者必須與小工具互動才能成功完成驗證。
只要在算繪 reCAPTCHA 前更新 Auth 執行個體上的語言代碼,即可根據使用者偏好設定,將基礎算繪的 reCAPTCHA 本地化。上述本地化設定也會套用至傳送給使用者的簡訊,內含驗證碼。
Web
import { getAuth } from "firebase/auth"; const auth = getAuth(); auth.languageCode = 'it'; // To apply the default browser preference instead of explicitly setting it. // auth.useDeviceLanguage();
Web
firebase.auth().languageCode = 'it'; // To apply the default browser preference instead of explicitly setting it. // firebase.auth().useDeviceLanguage();
使用隱形 reCAPTCHA
如要使用隱形 reCAPTCHA,請建立 RecaptchaVerifier 物件,並將 size 參數設為 invisible,然後指定提交登入表單的按鈕 ID。例如:
Web
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(); } });
Web
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', { 'size': 'invisible', 'callback': (response) => { // reCAPTCHA solved, allow signInWithPhoneNumber. onSignInSubmit(); } });
使用 reCAPTCHA 小工具
如要使用可見的 reCAPTCHA 小工具,請在網頁上建立元素來包含小工具,然後建立 RecaptchaVerifier 物件,並指定容器的 ID。例如:
Web
import { getAuth, RecaptchaVerifier } from "firebase/auth"; const auth = getAuth(); window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', {});
Web
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container');
選用:指定 reCAPTCHA 參數
您可以選擇在 RecaptchaVerifier 物件上設定回呼函式,在使用者解決 reCAPTCHA 或 reCAPTCHA 在使用者提交表單前過期時呼叫:
Web
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. // ... } });
Web
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:
Web
recaptchaVerifier.render().then((widgetId) => { window.recaptchaWidgetId = widgetId; });
Web
recaptchaVerifier.render().then((widgetId) => { window.recaptchaWidgetId = widgetId; });
render 解析後,您會取得 reCAPTCHA 的小工具 ID,可用於呼叫 reCAPTCHA API:
Web
const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);
Web
const recaptchaResponse = grecaptcha.getResponse(recaptchaWidgetId);
將驗證碼傳送至使用者的手機
如要啟動手機號碼登入程序,請向使用者顯示介面,提示他們提供手機號碼,然後呼叫 signInWithPhoneNumber,要求 Firebase 透過簡訊將驗證碼傳送至使用者的手機:
-
取得使用者的電話號碼。
法律規定因國家/地區而異,但為了提供最佳做法並讓使用者瞭解相關資訊,您應告知他們,如果使用電話號碼登入,可能會收到驗證簡訊,且須支付一般簡訊費用。
- 呼叫
signInWithPhoneNumber,並將使用者的電話號碼和先前建立的RecaptchaVerifier傳遞給該函式。如果Web
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 // ... });
Web
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 Authentication 將含有驗證碼的簡訊傳送至使用者的手機。
使用驗證碼登入使用者
signInWithPhoneNumber 呼叫成功後,請提示使用者輸入透過簡訊收到的驗證碼。接著,將程式碼傳遞至傳遞至 signInWithPhoneNumber 履行處理常式 (即 then 區塊) 的 ConfirmationResult 物件的 confirm 方法,登入使用者。例如:
Web
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?) // ... });
Web
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 模擬器或 Android 模擬器中開發,不必使用 Google Play 服務。
- 編寫整合測試時,不會受到通常在正式環境中對實際電話號碼套用的安全檢查阻礙。
虛構電話號碼必須符合下列規定:
- 請務必使用虛構的電話號碼,且該號碼不得已存在。 Firebase Authentication 不允許您將真實使用者使用的現有電話號碼設為測試號碼。 其中一個方法是使用以 555 為前置號碼的美國測試電話號碼,例如: +1 650-555-3434
- 電話號碼的長度和其他限制必須符合正確格式。系統仍會對這些號碼進行與真實使用者電話號碼相同的驗證。
- 最多可新增 10 個開發用電話號碼。
- 使用難以猜測的測試電話號碼/代碼,並經常變更。
建立虛構的電話號碼和驗證碼
- 在 Firebase 控制台中,開啟「驗證」部分。
- 在「登入方法」分頁中,啟用電話號碼供應商 (如果尚未啟用)。
- 開啟「測試用電話號碼」手風琴選單。
- 提供要測試的電話號碼,例如:+1 650-555-3434。
- 提供該號碼的 6 位數驗證碼,例如:654321。
- 新增電話號碼。如有需要,只要將游標懸停在對應列上,然後點按垃圾桶圖示,即可刪除電話號碼和驗證碼。
手動測試
您可以在應用程式中直接使用虛構電話號碼。這樣一來,您就能在開發階段執行手動測試,而不必擔心配額問題或節流。您也可以直接從 iOS 模擬器或 Android 模擬器進行測試,不必安裝 Google Play 服務。
提供虛構電話號碼並傳送驗證碼時,系統不會傳送實際的簡訊。您必須提供先前設定的驗證碼,才能完成登入。
登入完成後,系統會使用該電話號碼建立 Firebase 使用者。使用者行為和屬性與實際電話號碼使用者相同,存取 Realtime Database/Cloud Firestore 和其他服務的方式也相同。在此程序中產生的 ID 權杖,與實際電話號碼使用者的簽章相同。
如要進一步限制存取權,也可以透過這些使用者的自訂聲明設定測試角色,將他們區分為虛假使用者。
整合測試
除了手動測試之外,Firebase Authentication 也提供 API,協助您編寫電話號碼驗證測試的整合測試。這些 API 會停用應用程式驗證,方法是停用網頁中的 reCAPTCHA 需求,以及 iOS 中的無聲推播通知。因此您可以在這些流程中進行自動化測試,且實作起來更加容易。此外,這些 API 也可協助您在 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:透過
appVerifier.render()轉譯可見的 reCAPTCHA 時,系統會在短暫延遲後自動解決問題。這相當於使用者在 reCAPTCHA 顯示後立即點選。reCAPTCHA 回應會在一段時間後失效,然後再次自動解決。 - 隱形 reCAPTCHA:
隱形 reCAPTCHA 不會在轉譯時自動解決,而是在
appVerifier.verify()呼叫時,或在 reCAPTCHA 的按鈕錨點經過一小段延遲後點選時解決。同樣地,回應會在一段時間後失效,且只有在appVerifier.verify()呼叫後或再次點選 reCAPTCHA 的按鈕錨點時,才會自動解決。
每當模擬 reCAPTCHA 解決時,系統會如預期觸發對應的回呼函式,並提供虛假的回應。如果也指定了到期回呼,系統會在到期時觸發該回呼。
後續步驟
使用者首次登入後,系統會建立新的使用者帳戶,並連結至使用者登入時使用的憑證 (即使用者名稱和密碼、電話號碼或驗證供應商資訊)。這個新帳戶會儲存在 Firebase 專案中,可用於識別專案中每個應用程式的使用者,無論使用者登入方式為何。
-
在應用程式中,如要瞭解使用者的驗證狀態,建議在
Auth物件上設定觀察器。接著,您可以從User物件取得使用者的基本個人資料資訊。請參閱「管理使用者」。 在 Firebase Realtime Database 和 Cloud Storage 安全規則中,您可以從
auth變數取得已登入使用者的專屬使用者 ID, 並使用該 ID 控制使用者可存取的資料。
您可以將驗證供應商憑證連結至現有使用者帳戶,允許使用者透過多個驗證供應商登入應用程式。
如要登出使用者,請呼叫
signOut:
Web
import { getAuth, signOut } from "firebase/auth"; const auth = getAuth(); signOut(auth).then(() => { // Sign-out successful. }).catch((error) => { // An error happened. });
Web
firebase.auth().signOut().then(() => { // Sign-out successful. }).catch((error) => { // An error happened. });