您可以使用 Firebase Authentication 傳送簡訊來登入使用者 傳送到使用者的手機上使用者透過 簡訊。
如要在應用程式中新增電話號碼登入程序,最簡單的方法是使用 FirebaseUI, 包含用於實作手機登入流程的置入登入小工具 以及密碼和聯合登入。這份文件 說明如何使用 Firebase SDK 導入電話號碼登入流程。
事前準備
- 如果您尚未將應用程式連結至 Firebase 專案,請前往 Firebase 控制台。
-
使用 Swift Package Manager 安裝及管理 Firebase 依附元件。
- 在 Xcode 中保持開啟應用程式專案,然後前往「檔案」檔案 >新增套件。
- 在系統提示時,新增 Firebase Apple 平台 SDK 存放區:
- 選擇 Firebase Authentication 程式庫。
- 在目標建構設定的「Other Linker Flags」部分中新增
-ObjC
標記。 - 完成後,Xcode 會自動開始解析並下載 複製到背景依附元件
敬上https://github.com/firebase/firebase-ios-sdk.git
安全疑慮
僅使用電話號碼進行驗證,安全性較低 其他可用的方法,因為擁有電話號碼 可以在使用者之間輕鬆轉移此外,當多位使用者共用裝置時 設定檔,任何可接收簡訊的使用者,皆可透過 裝置的電話號碼。
如果您在應用程式中使用電話號碼登入功能,建議您提供該號碼 提供更安全的登入方式,並告知使用者這項安全措施 也不必為了使用電話號碼登入而做出取捨
為 Firebase 專案啟用電話號碼登入功能
您必須先啟用電話號碼登入功能,才能透過簡訊登入使用者 方法:
- 在 Firebase 控制台中開啟「驗證」專區。
- 在「Sign-in Method」(登入方式) 頁面中啟用電話號碼 登入方式。
啟用應用程式驗證功能
如要使用電話號碼驗證功能,Firebase 必須能夠驗證 電話號碼登入要求來自您的應用程式。與 GCP 互動的方式 Firebase Authentication 會完成下列操作:
- 靜音 APN 通知:您透過該使用者
首次在裝置上新增電話號碼,Firebase Authentication 會傳送
透過無訊息通知傳送到裝置。如果您的應用程式
已成功收到 Firebase 的通知,電話號碼
登入資訊就能繼續操作
如果是 iOS 8.0 以上版本,則靜音通知不需要明確設定 就算使用者拒絕接收同意聲明,也不會受到影響 APN 應用程式中的通知。因此,應用程式不需要求使用者提出要求 權限以便在實作 Firebase 手機時接收推播通知 驗證數字
- reCAPTCHA 驗證:如果應用程式收發郵件 無法傳送靜音推播通知 停用應用程式的背景重新整理功能,或是在 iOS 模擬工具,Firebase Authentication 使用 reCAPTCHA 驗證來完成 手機登入流程系統經常可以完成 reCAPTCHA 驗證問題 使用者不必解決任何問題
正確設定靜音推播通知後,只有極小的 百分比的使用者將看到 reCAPTCHA 流程。儘管如此 確保用於登入電話號碼的登入功能 (無論是否為靜音推送都能正確運作) 有通知。
開始接收靜音通知
如何啟用 APN 通知以便與 Firebase Authentication 搭配使用:
- 在 Xcode 中, 為專案啟用推播通知。
-
將 APN 驗證金鑰上傳至 Firebase。 如果您還沒有 APN 驗證金鑰,請務必前往 Apple Developer Member Center:
-
在 Firebase 控制台的專案中,選取 齒輪圖示,選取 「Project Settings」,然後選取 「雲端通訊」分頁。
-
在「iOS 應用程式設定」下方的「APNs 驗證金鑰」中, 按一下「上傳」圖示 按鈕。
-
瀏覽至儲存金鑰的位置並選取金鑰,然後按一下 開啟。加入金鑰的金鑰 ID (位於 Apple Developer Member Center 時,按一下 按一下「上傳」。
如果您已有 APN 憑證,則可上傳憑證 。
-
- 在 Xcode 中, 為專案啟用「背景模式」功能,然後勾選核取方塊 「背景擷取」和「遠端通知」模式。
設定 reCAPTCHA 驗證
如要啟用 Firebase SDK 進行 reCAPTCHA 驗證,請按照下列步驟操作:
- 在 Xcode 專案中新增自訂網址配置:
- 開啟專案設定:按兩下 左側樹狀檢視中從「目標」部分中選取應用程式,然後 選取「資訊」分頁標籤,然後展開「網址類型」部分。
- 點選「+」按鈕,然後將經過編碼的應用程式 ID 新增為網址
配置。如要找到經過編碼的應用程式 ID,請前往
一般
Firebase 控制台的「設定」頁面,位於 iOS 裝置的專區
應用程式。將其他欄位留白。
設定完成後,設定看起來應該會與 後面 (但採用您應用程式的專屬值):
- 選用:如要自訂應用程式的呈現方式
SFSafariViewController
(向使用者顯示 reCAPTCHA) 時建立自訂 符合AuthUIDelegate
通訊協定的類別,並傳遞至verifyPhoneNumber(_:uiDelegate:completion:)
。
將驗證碼傳送至使用者的手機
如要啟動電話號碼登入程序,請向使用者顯示提示的介面
他們提供電話號碼,然後撥打
verifyPhoneNumber(_:uiDelegate:completion:)
:要求 Firebase
透過簡訊傳送驗證碼到使用者的手機:
-
取得使用者的電話號碼。
法律要求各有不同,但我們建議的最佳做法是 並營造使用者的期待感,您應告知他們, 手機登入時,可能會收到驗證和標準簡訊 需支付簡訊費用。
- 呼叫
verifyPhoneNumber(_:uiDelegate:completion:)
並傳遞至 使用者的電話號碼。Swift
PhoneAuthProvider.provider() .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in if let error = error { self.showMessagePrompt(error.localizedDescription) return } // Sign in using the verificationID and the code sent to the user // ... }
Objective-C
[[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; return; } // Sign in using the verificationID and the code sent to the user // ... }];
verifyPhoneNumber
方法會重新傳送:如果您多次呼叫此方法,例如 在檢視畫面的onAppear
方法中,verifyPhoneNumber
方法不會 除非原始要求逾時,否則會發送第二封簡訊。呼叫
verifyPhoneNumber(_:uiDelegate:completion:)
時, Firebase 會將無訊息推播通知傳送至您的應用程式,或 向使用者提出 reCAPTCHA 驗證問題。應用程式收到 通知或使用者完成 reCAPTCHA 驗證問題,Firebase 傳送內含驗證碼的 SMS 訊息到 指定電話號碼,並將驗證 ID 傳送至 函式。您需要同時擁有驗證碼和驗證 以便登入使用者。您也能指定 Firebase 傳送的簡訊, 透過驗證上的
languageCode
屬性驗證語言 執行個體。Swift
// Change language code to french. Auth.auth().languageCode = "fr";
Objective-C
// Change language code to french. [FIRAuth auth].languageCode = @"fr";
-
儲存驗證 ID,並在應用程式載入時還原驗證 ID。獲得解答後 那麼,即使您的應用程式 在使用者完成登入流程前終止 (例如, 切換至簡訊應用程式)。
您可以視需求保留驗證 ID,方法很簡單 請使用
NSUserDefaults
物件儲存驗證 ID:Swift
UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
Objective-C
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:verificationID forKey:@"authVerificationID"];
然後,您可以還原已儲存的值:
Swift
let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
Objective-C
NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
如果呼叫 verifyPhoneNumber(_:uiDelegate:completion:)
驗證成功後,您可以提示使用者在輸入驗證碼時輸入驗證碼
透過簡訊接收。
透過驗證碼登入使用者
使用者提供應用程式傳送的驗證碼後
訊息,請建立 FIRPhoneAuthCredential
物件,並將該物件
至 signInWithCredential:completion:
。
- 向使用者取得驗證碼。
- 透過驗證建立
FIRPhoneAuthCredential
物件 和驗證 IDSwift
let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationID, verificationCode: verificationCode )
Objective-C
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID verificationCode:userInput];
- 使用
FIRPhoneAuthCredential
物件登入使用者身分:Swift
Auth.auth().signIn(with: credential) { authResult, error in if let error = error { let authError = error as NSError if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue { // The user is a multi-factor user. Second factor challenge is required. let resolver = authError .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver var displayNameString = "" for tmpFactorInfo in resolver.hints { displayNameString += tmpFactorInfo.displayName ?? "" displayNameString += " " } self.showTextInputPrompt( withMessage: "Select factor to sign in\n\(displayNameString)", completionBlock: { userPressedOK, displayName in var selectedHint: PhoneMultiFactorInfo? for tmpFactorInfo in resolver.hints { if displayName == tmpFactorInfo.displayName { selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo } } PhoneAuthProvider.provider() .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil, multiFactorSession: resolver .session) { verificationID, error in if error != nil { print( "Multi factor start sign in failed. Error: \(error.debugDescription)" ) } else { self.showTextInputPrompt( withMessage: "Verification code for \(selectedHint?.displayName ?? "")", completionBlock: { userPressedOK, verificationCode in let credential: PhoneAuthCredential? = PhoneAuthProvider.provider() .credential(withVerificationID: verificationID!, verificationCode: verificationCode!) let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator .assertion(with: credential!) resolver.resolveSignIn(with: assertion!) { authResult, error in if error != nil { print( "Multi factor finanlize sign in failed. Error: \(error.debugDescription)" ) } else { self.navigationController?.popViewController(animated: true) } } } ) } } } ) } else { self.showMessagePrompt(error.localizedDescription) return } // ... return } // User is signed in // ... }
Objective-C
[[FIRAuth auth] signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) { FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; NSMutableString *displayNameString = [NSMutableString string]; for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { [displayNameString appendString:tmpFactorInfo.displayName]; [displayNameString appendString:@" "]; } [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString] completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) { FIRPhoneMultiFactorInfo* selectedHint; for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { if ([displayName isEqualToString:tmpFactorInfo.displayName]) { selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo; } } [FIRPhoneAuthProvider.provider verifyPhoneNumberWithMultiFactorInfo:selectedHint UIDelegate:nil multiFactorSession:resolver.session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; } else { [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName] completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) { FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID verificationCode:verificationCode]; FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; } else { NSLog(@"Multi factor finanlize sign in succeeded."); } }]; }]; } }]; }]; } else if (error) { // ... return; } // User successfully signed in. Get user data from the FIRUser object if (authResult == nil) { return; } FIRUser *user = authResult.user; // ... }];
使用虛構的電話號碼進行測試
您可以透過 Firebase 控制台設定開發用的虛構電話號碼。使用虛構手機進行測試 數據具有以下優點:
- 測試電話號碼驗證,而不耗用您的使用配額。
- 在不傳送實際簡訊的情況下,測試電話號碼的驗證方式。
- 以相同的電話號碼連續執行測試,而不會受到限制。這個 如果未經審查人員在應用程式商店審查流程,將審查遭拒的風險降到最低 使用相同的電話號碼進行測試
- 在開發環境中輕鬆進行測試,不需額外的工作,例如 能夠在沒有 Google Play 服務的情況下,在 iOS 模擬器或 Android 模擬器中進行開發。
- 編寫整合測試,但這些測試不會因一般套用的安全性檢查功能而遭到封鎖 實際電話號碼。
虛構電話號碼必須符合下列規定:
- 請務必使用虛構的電話號碼,但實際電話號碼尚未存在。 Firebase Authentication 不允許將真人使用者目前使用的電話號碼設為測試號碼。 其中一種做法是使用 555 組前置字元為美國的測試電話號碼,例如: 0933 000 000
- 電話號碼的格式必須正確, 限制。不過,他們仍須完成與實際使用者電話號碼相同的驗證。
- 您最多可以新增 10 組開發用電話號碼。
- 使用不易猜到和變更的測試電話號碼/驗證碼 經常更新
建立虛構的電話號碼和驗證碼
- 在 Firebase 控制台中開啟 「驗證」區段。
- 在「登入方式」分頁中啟用電話服務供應商 (如果您尚未啟用的話)。
- 開啟「測試用電話號碼」選單。
- 提供要測試的電話號碼,例如 +1 650-555-3434。
- 提供該特定號碼的 6 位數驗證碼,例如「654321」。
- 新增數字。如有需要,你可以刪除電話號碼,以及 請將滑鼠遊標懸停在對應資料列上,再按一下垃圾桶圖示。
手動測試
您可以直接開始在應用程式中使用虛構的電話號碼。您可以藉此 執行手動測試,而不會發生配額問題或節流問題。 您也可以使用沒有 Google Play 服務的 iOS 模擬器或 Android 模擬器直接進行測試 已安裝。
如果您提供虛構的電話號碼並傳送驗證碼,則簡訊不會是 已傳送。您需要提供先前設定的驗證碼,才能完成簽署。 。
登入完成後,系統會使用該電話號碼建立 Firebase 使用者。 使用者和實際電話號碼使用者俱有相同的行為和屬性, Realtime Database/Cloud Firestore 和其他服務也是如此。建立期間建立的 ID 符記 這項程序與實際電話號碼使用者擁有相同的簽章。
另一種方式是透過自訂角色設定測試角色 聲明,以便區分他們為冒用使用者,以便進一步限制 資源存取權
整合測試
除了手動測試之外,Firebase Authentication 還提供 API,可協助您編寫整合測試 用於手機驗證測試這些 API 會停用 reCAPTCHA 來停用應用程式驗證功能 以及 iOS 的靜音推播通知這樣就能在自動化環境中執行自動化測試 也能輕鬆實作也用來測試 驗證流程
在 iOS 上,appVerificationDisabledForTesting
設定必須設為
在呼叫 verifyPhoneNumber
前為 TRUE
。這項作業不需要
在背景傳送任何 APN 權杖或傳送無訊息推播通知,
輸入「Google 查詢」。這麼做也會停用 reCAPTCHA 備用流程。
請注意,停用應用程式驗證功能後,系統會使用非虛構的電話號碼 無法完成登入。這個 API 只能使用虛構的電話號碼。
Swift
let phoneNumber = "+16505554567" // This test verification code is specified for the given test phone number in the developer console. let testVerificationCode = "123456" Auth.auth().settings.isAppVerificationDisabledForTesting = TRUE PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate:nil) { verificationID, error in if (error) { // Handles error self.handleError(error) return } let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID ?? "", verificationCode: testVerificationCode) Auth.auth().signInAndRetrieveData(with: credential) { authData, error in if (error) { // Handles error self.handleError(error) return } _user = authData.user }]; }];
Objective-C
NSString *phoneNumber = @"+16505554567"; // This test verification code is specified for the given test phone number in the developer console. NSString *testVerificationCode = @"123456"; [FIRAuth auth].settings.appVerificationDisabledForTesting = YES; [[FIRPhoneAuthProvider provider] verifyPhoneNumber:phoneNumber completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) { if (error) { // Handles error [self handleError:error]; return; } FIRAuthCredential *credential = [FIRPhoneAuthProvider credentialWithVerificationID:verificationID verificationCode:testVerificationCode]; [FIRAuth auth] signInWithAndRetrieveDataWithCredential:credential completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { if (error) { // Handles error [self handleError:error]; return; } _user = user; }]; }];
附錄:在不移位的情況下使用手機登入
Firebase Authentication 會使用方法滑動功能,自動取得應用程式的 APN 權杖,用於處理 Firebase 傳送給 並自動攔截從 驗證期間的 reCAPTCHA 驗證頁面。
如果您不想使用清除功能,只要新增標記即可停用
FirebaseAppDelegateProxyEnabled
加入應用程式的 Info.plist 檔案,
設為 NO
請注意,將此標記設為 NO
也會停用其他 Firebase 產品的滑動功能,包括
Firebase Cloud Messaging。
停用滑動功能時,必須明確傳送 APN 裝置權杖 推播通知,以及指向 Firebase Authentication 的自訂配置重新導向網址。
如果您要建構 SwiftUI 應用程式,還必須明確傳送 APNs 裝置權杖 推播通知,以及指向 Firebase Authentication 的自訂配置重新導向網址。
如要取得 APN 裝置權杖,請實作
application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
方法,然後將裝置權杖傳遞至 Auth
的
setAPNSToken(_:type:)
方法。
Swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Pass device token to auth Auth.auth().setAPNSToken(deviceToken, type: .unknown) // Further handling of the device token if needed by the app // ... }
Objective-C
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // Pass device token to auth. [[FIRAuth auth] setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeProd]; // Further handling of the device token if needed by the app. }
如要處理推播通知,請在
application(_:didReceiveRemoteNotification:fetchCompletionHandler:):
方法後,請呼叫
Auth
的 canHandleNotification(_:)
方法。
Swift
func application(_ application: UIApplication, didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if Auth.auth().canHandleNotification(notification) { completionHandler(.noData) return } // This notification is not auth related; it should be handled separately. }
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // Pass notification to auth and check if they can handle it. if ([[FIRAuth auth] canHandleNotification:notification]) { completionHandler(UIBackgroundFetchResultNoData); return; } // This notification is not auth related; it should be handled separately. }
如要處理自訂配置重新導向網址,請將
application(_:open:options:)
方法,然後在這些方法中,將網址傳遞至 Auth
的
canHandleURL(_:)
方法。
Swift
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { if Auth.auth().canHandle(url) { return true } // URL not auth related; it should be handled separately. }
Objective-C
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } // URL not auth related; it should be handled separately. }
如果您使用 SwiftUI 或 UISceneDelegate
來處理重新導向網址,請採用
scene(_:openURLContexts:)
方法,然後在這些方法中,將網址傳遞至 Auth
的
canHandleURL(_:)
方法。
Swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { for urlContext in URLContexts { let url = urlContext.url _ = Auth.auth().canHandle(url) } // URL not auth related; it should be handled separately. }
Objective-C
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.url]; // URL not auth related; it should be handled separately. } }
後續步驟
使用者首次登入後,系統會建立新的使用者帳戶 也就是使用者的名稱和密碼 號碼或驗證提供者資訊,也就是使用者登入時使用的網址。這項新功能 帳戶儲存為 Firebase 專案的一部分,可用來識別 即可限制使用者登入專案中的所有應用程式
在你的Firebase Realtime Database和Cloud Storage中 查看安全性規則 透過
auth
變數取得已登入使用者的不重複使用者 ID。 控管使用者可以存取的資料
您可以讓使用者透過多重驗證機制登入您的應用程式 將驗證供應商憑證連結至 現有的使用者帳戶
如要登出使用者,請呼叫
signOut:
。
Swift
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
Objective-C
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
我們也建議您新增錯誤處理程式碼,適用於完整的驗證範圍 發生錯誤。請參閱處理錯誤。