您可以使用 Firebase 驗證功能,傳送簡訊至使用者的手機來登入使用者。使用者以簡訊中的一次性代碼登入。
在應用程式中新增電話號碼登入最簡單的方法,就是使用 FirebaseUI,其中包含的登入小工具會實作電話號碼登入的登入流程,以及密碼和聯合登入。本文件說明如何使用 Firebase SDK 導入電話號碼登入流程。
事前準備
如果您尚未將初始化程式碼片段從 Firebase 控制台複製到您的專案,請按照「 將 Firebase 新增至 JavaScript 專案」的說明操作。安全疑慮
只用電話號碼進行驗證,但安全性會比其他可用方法低,因為擁有電話號碼可在使用者之間輕鬆轉移。此外,在設有多個使用者設定檔的裝置上,任何能夠接收簡訊的使用者,都能使用裝置的電話號碼登入帳戶。
如果您在應用程式中使用電話號碼登入,除了要另外提供更安全的登入方式外,也應告知使用者使用電話號碼登入的安全防護措施。
啟用 Firebase 專案的電話號碼登入功能
如要透過簡訊登入使用者,您必須先在 Firebase 專案中啟用電話號碼登入方法:
- 在 Firebase 控制台,開啟「驗證」部分。
- 在「登入方式」頁面上,啟用「電話號碼」登入方式。
- 在同一個頁面中,如果負責代管應用程式的網域未列在「OAuth 重新導向網域」部分中,請新增您的網域。請注意,系統不允許為了電話驗證而使用 localhost 做為代管網域。
Firebase 的電話號碼登入要求配額夠高,因此大多數應用程式都不會受到影響。不過,如果您需要進行電話驗證來登入大量使用者,則可能需要升級定價方案。請參閱定價頁面。
設定 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 驗證要求將含有驗證碼的簡訊傳送至使用者的手機。
透過驗證碼讓使用者登入
呼叫 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 控制台為開發作業設定虛構的電話號碼。以虛構電話號碼進行測試具有以下優點:
- 測試電話號碼驗證,而不必消耗用量配額。
- 測試電話號碼的驗證作業,不必實際傳送簡訊。
- 用同一組電話號碼連續進行測試,不受節流限制。如果審查人員剛好使用相同的電話號碼進行測試,這種做法可在應用程式商店審查過程中,盡可能降低應用程式遭拒的風險。
- 您不必採取額外措施,就能在開發環境中直接進行測試,例如在沒有 Google Play 服務的 iOS 模擬器中開發應用程式,或是在沒有 Google Play 服務的 Android 模擬器中進行開發。
- 編寫整合測試,而不必遭到系統封鎖,因為在正式環境中對實際電話號碼套用安全性檢查。
虛構電話號碼必須符合下列規定:
- 請務必使用虛構且不存在的電話號碼。透過 Firebase 驗證功能,你無法將實際使用者所用的現有電話號碼設為測試號碼。其中一種做法是使用 555 前置字元的電話號碼做為美國測試電話號碼,例如:+1 650-555-3434
- 電話號碼必須採用正確的長度格式和其他限制。不過,他們仍可進行驗證,與實際使用者的電話號碼一樣。
- 您最多可以新增 10 組開發用電話號碼。
- 請使用難以猜測及經常變更的測試電話號碼/代碼。
產生虛構電話號碼和驗證碼
- 在 Firebase 控制台,開啟「驗證」專區。
- 在「Sign in method」分頁中,啟用電話供應商 (如果尚未啟用)。
- 開啟「用於測試的電話號碼」摺疊式選單。
- 提供要測試的電話號碼,例如:+1 650-555-3434。
- 提供該特定號碼的 6 位數驗證碼,例如:654321。
- 新增號碼。如要刪除電話號碼及其代碼,請將滑鼠遊標懸停在對應的資料列上,然後按一下垃圾桶圖示。
手動測試
您可以直接在應用程式中使用虛構的電話號碼。這可讓您在開發階段執行手動測試,而不會遇到配額問題或節流情形。您可以在未安裝 Google Play 服務的情況下,直接透過 iOS 模擬器或 Android 模擬器進行測試。
當您提供虛構電話號碼並傳送驗證碼時,系統不會傳送任何實際簡訊。您必須改為提供先前設定的驗證碼,才能完成登入。
使用者登入時,系統會使用該電話號碼建立 Firebase 使用者。使用者與實際電話號碼使用者俱有相同的行為和屬性,也能以同樣的方式存取即時資料庫/Cloud Firestore 與其他服務。在這個過程中壓縮的 ID 權杖,其簽章與實際電話號碼使用者相同。
另一種做法是透過自訂憑證對這些使用者設定測試角色,藉此區分這些使用者為假使用者,以便進一步限制存取權。
整合測試
除了手動測試外,Firebase 驗證也提供 API,協助您編寫手機驗證測試的整合測試。這些 API 會在網頁停用 reCAPTCHA 要求,並在 iOS 中停用靜音推播通知,藉此停用應用程式驗證功能。這樣就能在這些流程中實現自動化測試,並更輕鬆地實作。此外,這些 API 還能用來測試 Android 上的即時驗證流程。
在網站算繪 firebase.auth.RecaptchaVerifier
前,請將 appVerificationDisabledForTesting
設為 true
。這可自動解決 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 即時資料庫和 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. });