Catch up on everthing we announced at this year's Firebase Summit. Learn more

使用電話號碼在 Apple 平台上通過 Firebase 進行身份驗證

您可以使用 Firebase 身份驗證通過向用戶的手機發送 SMS 消息來登錄用戶。用戶使用 SMS 消息中包含的一次性代碼登錄。

最簡單的方法來添加電話號碼,登錄到您的應用程序是使用FirebaseUI ,其中包括一個下拉在登錄窗口小部件實現簽到流的電話號碼登錄,以及基於密碼的和聯合的標誌-在。本文檔介紹瞭如何使用 Firebase SDK 實現電話號碼登錄流程。

在你開始之前

使用 Swift Package Manager 安裝和管理 Firebase 依賴項。

  1. 在Xcode中,您的應用項目打開,導航到File>斯威夫特包>添加包的依賴
  2. 出現提示時,添加 Firebase Apple 平台 SDK 存儲庫:
  3.   https://github.com/firebase/firebase-ios-sdk
      
  4. 選擇 Firebase 身份驗證庫。
  5. 完成後,Xcode 將在後台自動開始解析和下載您的依賴項。
並且,檢查配置步驟:
  1. 如果您還沒有連接您的應用程序到你的火力地堡項目,從這樣做火力地堡控制台

安全問題

僅使用電話號碼的身份驗證雖然方便,但不如其他可用方法安全,因為電話號碼的所有權很容易在用戶之間轉移。此外,在具有多個用戶配置文件的設備上,任何可以接收 SMS 消息的用戶都可以使用設備的電話號碼登錄帳戶。

如果您在應用中使用基於電話號碼的登錄,則應將其與更安全的登錄方法一起提供,並告知用戶使用電話號碼登錄的安全性權衡。

為您的 Firebase 項目啟用電話號碼登錄

要通過短信登錄用戶,您必須首先為您的 Firebase 項目啟用電話號碼登錄方法:

  1. 火力地堡控制台,打開驗證部分。
  2. 登錄方法頁,使電話號碼登錄方法。

Firebase 的電話號碼登錄請求配額足夠高,大多數應用不會受到影響。但是,如果您需要使用電話身份驗證登錄大量用戶,則可能需要升級定價計劃。請參閱定價頁面。

啟用應用驗證

要使用電話號碼身份驗證,Firebase 必須能夠驗證電話號碼登錄請求是否來自您的應用。 Firebase 身份驗證有兩種方式來實現這一點:

  • 無聲的APN通知:當您在與首次在設備上自己的手機號碼的用戶註冊,認證火力地堡發送一個令牌使用無聲推送通知的設備。如果您的應用成功收到來自 Firebase 的通知,則可以繼續進行電話號碼登錄。

    對於 iOS 8.0 及更高版本,靜默通知不需要明確的用戶同意,因此不受用戶拒絕在應用程序中接收 APNs 通知的影響。因此,在實施 Firebase 電話號碼身份驗證時,應用程序不需要請求用戶許可來接收推送通知。

  • 驗證碼驗證:在事件發送或接收無聲推送通知是不可能的,當用戶禁用背景刷新你的應用程序,例如,或在iOS模擬器測試您的應用程序時,火力地堡身份驗證使用驗證碼驗證完成手機簽到流程。 reCAPTCHA 挑戰通常可以在用戶無需解決任何問題的情況下完成。

正確配置靜默推送通知後,只有極少數用戶會體驗 reCAPTCHA 流程。儘管如此,您應該確保電話號碼登錄功能正常運行,無論靜默推送通知是否可用。

開始接收靜默通知

要啟用 APNs 通知以用於 Firebase 身份驗證:

  1. 在Xcode中,啟用推送通知,為您的項目。
  2. 將您的 APNs 身份驗證密鑰上傳到 Firebase。如果你不已經有一個APN的認證密鑰,確保在創建一個蘋果開發者會員中心

    1. 在火力地堡控制台你的項目中,選擇齒輪圖標,選擇項目設置,然後選擇雲端通訊標籤。

    2. 在根據iOS應用配置的APN認證密鑰,點擊上傳按鈕。

    3. 瀏覽到您保存密鑰的位置,選擇它,然後單擊打開。添加鍵ID為鍵(可在蘋果開發者會員中心),然後單擊上傳

    如果您已有 APNs 證書,則可以上傳該證書。

設置 reCAPTCHA 驗證

要啟用 Firebase SDK 以使用 reCAPTCHA 驗證:

  1. 將自定義 URL 方案添加到您的 Xcode 項目:
    1. 打開您的項目配置:雙擊左側樹視圖中的項目名稱。從選擇目標部分的應用程序,然後選擇信息選項卡,並展開URL類型部分。
    2. 點擊+按鈕,添加一個網址方案,為您的反向客戶端ID。為了找到這個值,打開GoogleService-Info.plist的配置文件,並查找REVERSED_CLIENT_ID關鍵。複製鍵的值,並將它粘貼到URL方案的配置頁面框。將其他字段留空。

      完成後,您的配置應類似於以下內容(但具有特定於應用程序的值):

  2. 可選:如果您想自定義您的應用程序的呈現方式SFSafariViewControllerUIWebView顯示的驗證碼給用戶時,創建一個自定義類,符合的FIRAuthUIDelegate協議,並把它傳遞給verifyPhoneNumber:UIDelegate:completion:

向用戶手機發送驗證碼

要啟動手機號碼登錄,顯示用戶界面,提示他們提供他們的電話號碼,然後打電話verifyPhoneNumber:UIDelegate:completion:到請求火力地堡通過短信發送驗證碼到用戶的手機:

  1. 獲取用戶的電話號碼。

    法律要求各不相同,但作為最佳實踐並為您的用戶設定期望,您應該通知他們,如果他們使用電話登錄,他們可能會收到一條 SMS 消息進行驗證,並且適用標準費率。

  2. 呼叫verifyPhoneNumber:UIDelegate:completion: ,將其傳遞給用戶的電話號碼。

    迅速

    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
          // ...
      }

    目標-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:UIDelegate:completion: ,火力地堡發送無聲推送通知到你的應用程序,或發出reCAPTCHA驗證碼給用戶。在您的應用收到通知或用戶完成 reCAPTCHA 質詢後,Firebase 會向指定的電話號碼發送一條包含身份驗證代碼的 SMS 消息,並將驗證 ID 傳遞給您的完成功能。您將需要驗證碼和驗證 ID 才能登錄用戶。

    由火力地堡發送的短消息也可以通過通過指定AUTH語言本地化languageCode在你驗證實例屬性。

    迅速

     // Change language code to french.
     Auth.auth().languageCode = "fr";
    

    目標-C

     // Change language code to french.
     [FIRAuth auth].languageCode = @"fr";
    
  3. 保存驗證 ID 並在您的應用加載時恢復它。通過這樣做,如果您的應用程序在用戶完成登錄流程之前終止(例如,切換到 SMS 應用程序時),您可以確保您仍然擁有有效的驗證 ID。

    您可以以任何方式保留驗證 ID。一個簡單的方法是保存的驗證ID與NSUserDefaults對象:

    迅速

    UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
    

    目標-C

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:verificationID forKey:@"authVerificationID"];
    

    然後,您可以恢復保存的值:

    迅速

    let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
    

    目標-C

    NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
    

如果調用verifyPhoneNumber:UIDelegate:completion:成功,則可以提示用戶,當他們收到的短信輸入驗證碼。

使用驗證碼登錄用戶

用戶提供從SMS消息的驗證碼應用程式後,通過創建一個登錄用戶FIRPhoneAuthCredential從驗證碼和核查ID對象並傳遞該對象signInWithCredential:completion:

  1. 從用戶處獲取驗證碼。
  2. 創建FIRPhoneAuthCredential從驗證碼和核查ID對象。

    迅速

    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationID,
      verificationCode: verificationCode
    )

    目標-C

    FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider]
        credentialWithVerificationID:verificationID
                    verificationCode:userInput];
  3. 登錄與用戶FIRPhoneAuthCredential對象:

    迅速

    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
        // ...
    }

    目標-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 控制台為開發設置虛構的電話號碼。使用虛構的電話號碼進行測試具有以下好處:

  • 在不消耗您的使用配額的情況下測試電話號碼身份驗證。
  • 在不發送實際 SMS 消息的情況下測試電話號碼身份驗證。
  • 使用相同的電話號碼運行連續測試而不會受到限制。如果審閱者碰巧使用相同的電話號碼進行測試,這可以最大限度地降低 App Store 審閱過程中被拒絕的風險。
  • 無需任何額外工作即可在開發環境中輕鬆測試,例如無需 Google Play 服務即可在 iOS 模擬器或 Android 模擬器中進行開發的能力。
  • 編寫集成測試,而不會被生產環境中通常應用於真實電話號碼的安全檢查所阻止。

虛構的電話號碼必須滿足以下要求:

  1. 確保您使用的電話號碼確實是虛構的,並且不存在。 Firebase 身份驗證不允許您將真實用戶使用的現有電話號碼設置為測試號碼。一種選擇是使用555前綴號碼作為美國測試的電話號碼,例如:+1 650-555-3434
  2. 電話號碼的格式必須正確以適應長度和其他限制。他們仍將通過與真實用戶電話號碼相同的驗證。
  3. 您最多可以添加 10 個電話號碼進行開發。
  4. 使用難以猜測的測試電話號碼/代碼並經常更改它們。

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

  1. 火力地堡控制台,打開驗證部分。
  2. 在登錄方法選項卡,使手機供應商,如果你還沒有準備好。
  3. 打開進行測試手風琴菜單的電話號碼
  4. 提供要測試的電話號碼,例如:+1 650-555-3434。
  5. 提供一種用於特定數目的6位數驗證碼,例如:654321。
  6. 添加的數量。如果需要,您可以通過將鼠標懸停在相應行上並單擊垃圾桶圖標來刪除電話號碼及其代碼。

手動測試

您可以直接開始在您的應用程序中使用虛構的電話號碼。這使您可以在開發階段執行手動測試,而不會遇到配額問題或限制。您還可以在未安裝 Google Play 服務的情況下直接從 iOS 模擬器或 Android 模擬器進行測試。

當您提供虛構的電話號碼並發送驗證碼時,不會發送實際的 SMS。相反,您需要提供之前配置的驗證碼才能完成登錄。

登錄完成後,會使用該電話號碼創建 Firebase 用戶。用戶具有與真實電話號碼用戶相同的行為和屬性,並且可以以相同的方式訪問實時數據庫/Cloud Firestore 和其他服務。在此過程中鑄造的 ID 令牌具有與真實電話號碼用戶相同的簽名。

另一種選擇是設置一個測試角色通過自定義的權利要求對這些用戶他們區分假冒的用戶,如果你想進一步限制訪問。

集成測試

除了手動測試之外,Firebase 身份驗證還提供 API 來幫助編寫電話身份驗證測試的集成測試。這些 API 通過禁用 Web 中的 reCAPTCHA 要求和 iOS 中的靜默推送通知來禁用應用驗證。這使得在這些流程中進行自動化測試成為可能並且更容易實現。此外,它們有助於提供在 Android 上測試即時驗證流程的能力。

在iOS上,該appVerificationDisabledForTesting設置必須被設置為TRUE調用之前verifyPhoneNumber 。無需任何 APNs 令牌或在後台發送靜默推送通知即可處理此操作,從而更容易在模擬器中進行測試。這也會禁用 reCAPTCHA 回退流程。

請注意,當應用驗證被禁用時,使用非虛構電話號碼將無法完成登錄。此 API 只能使用虛構電話號碼。

迅速

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
    }];
}];

目標-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;
    }];
}];

附錄:使用手機登錄而不使用 swizzling

Firebase 身份驗證使用方法 swizzling 自動獲取您應用的 APNs 令牌,處理 Firebase 發送給您的應用的靜默推送通知,並在驗證期間自動攔截來自 reCAPTCHA 驗證頁面的自定義方案重定向。

如果你不喜歡使用混寫,你可以通過添加標誌禁用它FirebaseAppDelegateProxyEnabled到你的應用程序的Info.plist文件,並將其設置為NO 。請注意,設置該標誌以NO也禁止混寫其他火力地堡產品,包括火力地堡雲端通訊。

如果禁用 swizzling,則必須將 APNs 設備令牌、推送通知和自定義方案重定向 URL 顯式傳遞給 Firebase 身份驗證。

以獲得的APN設備令牌,實現application:didRegisterForRemoteNotificationsWithDeviceToken:方法,以及在其中,通過該設備令牌FIRAuthsetAPNSToken:type:方法。

迅速

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  // Pass device token to auth
  Auth.auth().setAPNSToken(deviceToken, type: .prod)

  // Further handling of the device token if needed by the app
  // ...
}

目標-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通過調用相關的通知FIRAuthcanHandleNotification:方法。

迅速

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, developer should handle it.
}

目標-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, developer should handle it.
}

為了處理自定義方案重定向URL,實現application:openURL:sourceApplication:annotation:方法運行iOS 8及以上,以及設備application:openURL:options:對於運行iOS 9和更新,並在他們的設備的方法,通過網址FIRAuthcanHandleURL方法。

迅速

// For iOS 9+
func application(_ application: UIApplication, open url: URL,
    options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
  if Auth.auth().canHandle(url) {
    return true
  }
  // URL not auth related, developer should handle it.
}

// For iOS 8-
func application(_ application: UIApplication,
                 open url: URL,
                 sourceApplication: String?,
                 annotation: Any) -> Bool {
  if Auth.auth().canHandle(url) {
    Return true
  }
  // URL not auth related, developer should handle it.
}

目標-C

// For iOS 9+
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  if ([[FIRAuth auth] canHandleURL:url]) {
    return YES;
  }
  // URL not auth related, developer should handle it.
}

// For iOS 8-
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
  if ([[FIRAuth auth] canHandleURL:url]) {
    return YES;
  }
  // URL not auth related, developer should handle it.
}

如果你使用SwiftUI或UISceneDelegate ,來處理重定向URL,實現scene:openURLContexts:方法,並在其中,將URL傳遞FIRAuthcanHandleURL方法。

迅速

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  for urlContext in URLContexts {
      let url = urlContext.url
      Auth.auth().canHandle(url)
  }
  // URL not auth related, developer should handle it.
}

目標-C

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
  for (UIOpenURLContext *urlContext in URLContexts) {
    [FIRAuth.auth canHandleURL:urlContext.url];
    // URL not auth related, developer should handle it.
  }
}

下一步

用戶首次登錄後,將創建一個新用戶帳戶並將其鏈接到用戶登錄時使用的憑據(即用戶名和密碼、電話號碼或身份驗證提供商信息)。這個新帳戶作為 Firebase 項目的一部分存儲,可用於識別項目中每個應用中的用戶,無論用戶如何登錄。

  • 在您的應用程序,你可以從用戶的基本資料信息FIRUser對象。請參閱管理用戶

  • 在你的火力地堡實時數據庫和雲存儲安全規則,你可以得到簽署的,從用戶的唯一的用戶ID auth的變量,並用它來控制哪些數據的用戶可以訪問。

您可以允許用戶通過使用多個身份驗證提供登錄到您的應用程序連接身份驗證提供憑據到現有的用戶帳戶。

要註銷用戶,請撥打signOut:

迅速

    let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}
  

目標-C

    NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

您可能還想為所有身份驗證錯誤添加錯誤處理代碼。請參見處理錯誤