您可以使用 Firebase Authentication 將簡訊傳送至使用者的手機,讓使用者登入。使用者使用簡訊訊息中包含的一次性代碼登入。
在應用程式中新增電話號碼登入功能最簡單的方法,就是使用 FirebaseUI,其中包含置入式登入小工具,可實作電話號碼登入、密碼和聯合登入的登入流程。本文說明如何使用 Firebase SDK 導入電話號碼登入流程。
事前準備
如果您尚未這麼做,請按照「 將 Firebase 新增至 JavaScript 專案」一文所述,將初始化程式碼片段從 Firebase 主控台複製到專案。安全疑慮
雖然只使用電話號碼進行驗證很方便,但比起其他可用方法,安全性較低,因為電話號碼很容易在使用者之間轉移。此外,在有多個使用者設定檔的裝置上,任何可接收簡訊的使用者都可以使用裝置的電話號碼登入帳戶。
如果您在應用程式中使用電話號碼登入功能,應同時提供更安全的登入方式,並告知使用者使用電話號碼登入功能的安全性取捨。
為 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
傳遞給signInWithPhoneNumber
。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
成功後,請提示使用者輸入透過簡訊收到的驗證碼。接著,將程式碼傳遞至 ConfirmationResult
物件的 confirm
方法,該方法已傳遞至 signInWithPhoneNumber
的服務處理常式 (即其 then
區塊)。例如:
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。
- 新增電話號碼。如有需要,您可以將滑鼠游標懸停在對應的資料列,然後按一下垃圾桶圖示,即可刪除電話號碼和代碼。
手動測試
您可以在應用程式中直接開始使用虛構電話號碼。這樣一來,您就能在開發階段執行手動測試,而不必擔心會發生配額問題或頻寬限制。您也可以直接在未安裝 Google Play 服務的 iOS 模擬器或 Android 模擬器中進行測試。
當您提供虛構的電話號碼並傳送驗證碼時,系統不會傳送實際的簡訊。您必須提供先前設定的驗證碼,才能完成登入程序。
登入完成後,系統會使用該電話號碼建立 Firebase 使用者。使用者具有與實際電話號碼使用者相同的行為和屬性,且可透過相同方式存取 Realtime Database/Cloud Firestore 和其他服務。這個程序中產生的 ID 權杖,其簽章與真實電話號碼使用者相同。
如果您想進一步限制存取權,另一個做法是透過自訂宣告設定測試角色,將這些使用者視為假使用者。
整合測試
除了手動測試之外,Firebase Authentication 也提供 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 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. });