使用 Apple 和 C++ 進行驗證

您可以讓使用者透過 Apple ID 向 Firebase 進行驗證,步驟如下: 並使用 Firebase SDK 執行端對端 OAuth 2.0 登入流程。

事前準備

如要使用 Apple 登入使用者,請先設定「使用 Apple 帳戶登入」 然後啟用 Apple 做為您的登入服務供應商, Firebase 專案。

加入 Apple Developer Program

只有 Apple Developer 成員可以設定「使用 Apple 帳戶登入」 計畫

設定「使用 Apple 登入」功能

您必須在 Firebase 專案中啟用 Apple 登入功能,並完成相關設定。 設定方法會因 Android 和 Apple 平台而異。請按照 「設定使用 Apple 登入」的 Apple 平台和/或 使用 Android 指南 繼續。

啟用 Apple 做為登入服務供應商

  1. Firebase 控制台中開啟「Auth」專區。在「Sign in method」分頁中: 啟用 Apple 供應商。
  2. 調整 Apple 登入提供者設定:
    1. 如果您只透過 Apple 平台部署應用程式,可以將 服務 ID、Apple 團隊 ID、私密金鑰和金鑰 ID 欄位空白。
    2. 如需 Android 裝置的支援:
      1. 將 Firebase 新增至您的 Android 專案。成為 請務必使用應用程式的 SHA-1 簽章,在 Firebase 控制台。
      2. Firebase 控制台,開啟「驗證」專區。在「Sign in method」分頁中: 啟用 Apple 供應商。指定您於 請參閱上一節的說明此外,在「OAuth 程式碼流程設定」部分中 指定 Apple Team ID,以及您建立的私密金鑰和金鑰 ID 先前的章節

遵守 Apple 的去識別化資料規定

「使用 Apple 帳戶登入」功能可讓使用者選擇將資料去識別化、 包括電子郵件地址選擇這個選項的使用者 擁有 privaterelay.appleid.com 網域的電子郵件地址。時間 如果您在應用程式中使用「使用 Apple 帳戶登入」功能,您必須遵守所有適用的 有關這些匿名 Apple 的開發人員政策或條款 而非客戶 ID

包括事先取得任何必要的使用者同意聲明 將任何直接識別的個人資訊與去識別化的 Apple 建立關聯 編號。使用 Firebase 驗證時,這可能包括: 動作:

  • 將電子郵件地址連結至去識別化的 Apple ID。
  • 將電話號碼與去識別化的 Apple ID 連結
  • 將非匿名的社會憑證 (Facebook、Google 等) 連結至 或匿名化 Apple ID。

請注意,上方清單僅列出部分示例。參閱 Apple Developer Program 《授權協議》 確認您的應用程式符合 Apple 的規定。

存取 firebase::auth::Auth 類別

Auth 類別是所有 API 呼叫的閘道,
  1. 新增驗證與應用程式標頭檔案:
    #include "firebase/app.h"
    #include "firebase/auth.h"
    
  2. 在初始化程式碼中,建立 firebase::App 類別。
    #if defined(__ANDROID__)
      firebase::App* app =
          firebase::App::Create(firebase::AppOptions(), my_jni_env, my_activity);
    #else
      firebase::App* app = firebase::App::Create(firebase::AppOptions());
    #endif  // defined(__ANDROID__)
    
  3. 取得 firebase::Appfirebase::auth::Auth 類別。 AppAuth 之間有一對一的對應關係。
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
    

使用 Firebase SDK 處理登入流程

透過 Apple 登入的程序會因 Apple 和 Android 平台而異。

在 Apple 平台上

透過 Apple 登入功能,透過 Firebase 驗證使用者 從 C++ 程式碼叫用的 Objective-C SDK。

  1. 針對每個登入要求產生隨機字串— 「nonce」,您可以用來確保取得的 ID 權杖 。這個 步驟非常重要,即可預防重送攻擊

      - (NSString *)randomNonce:(NSInteger)length {
        NSAssert(length > 0, @"Expected nonce to have positive length");
        NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
        NSMutableString *result = [NSMutableString string];
        NSInteger remainingLength = length;
    
        while (remainingLength > 0) {
          NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
          for (NSInteger i = 0; i < 16; i++) {
            uint8_t random = 0;
            int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
            NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
    
            [randoms addObject:@(random)];
          }
    
          for (NSNumber *random in randoms) {
            if (remainingLength == 0) {
              break;
            }
    
            if (random.unsignedIntValue < characterSet.length) {
              unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
              [result appendFormat:@"%C", character];
              remainingLength--;
            }
          }
        }
      }
    
    

    您會在登入要求時傳送 Nonce 的 SHA256 雜湊, Apple 會在回應中傳遞無變更。Firebase 會驗證回應 ,方法是對原始 Nonce 雜湊,並與 Apple 傳送的值進行比較。

  2. 啟動 Apple 的登入流程,包括您要求中的 Nonce 和負責處理 Apple 回應的委派類別 (請參閱 下一步):

      - (void)startSignInWithAppleFlow {
        NSString *nonce = [self randomNonce:32];
        self.currentNonce = nonce;
        ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
        ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest];
        request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
        request.nonce = [self stringBySha256HashingString:nonce];
    
        ASAuthorizationController *authorizationController =
            [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
        authorizationController.delegate = self;
        authorizationController.presentationContextProvider = self;
        [authorizationController performRequests];
      }
    
      - (NSString *)stringBySha256HashingString:(NSString *)input {
        const char *string = [input UTF8String];
        unsigned char result[CC_SHA256_DIGEST_LENGTH];
        CC_SHA256(string, (CC_LONG)strlen(string), result);
    
        NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
        for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
          [hashed appendFormat:@"%02x", result[i]];
        }
        return hashed;
      }
    
  3. 在實作 ASAuthorizationControllerDelegate`。如果登入成功,請使用 ID 這個符記應用於 Firebase:

      - (void)authorizationController:(ASAuthorizationController *)controller
         didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) {
        if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
          ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential;
          NSString *rawNonce = self.currentNonce;
          NSAssert(rawNonce != nil, @"Invalid state: A login callback was received, but no login request was sent.");
    
          if (appleIDCredential.identityToken == nil) {
            NSLog(@"Unable to fetch identity token.");
            return;
          }
    
          NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken
                                                    encoding:NSUTF8StringEncoding];
          if (idToken == nil) {
            NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken);
          }
        }
    
  4. 使用產生的權杖字串和原始 Nonce 建構 Firebase 憑證並登入 Firebase。

    firebase::auth::OAuthProvider::GetCredential(
            /*provider_id=*/"apple.com", token, nonce,
            /*access_token=*/nullptr);
    
    firebase::Future<firebase::auth::AuthResult> result =
        auth->SignInAndRetrieveDataWithCredential(credential);
    
  5. 相同的模式可與 Reauthenticate 搭配使用 用來為需要修改的敏感作業擷取新憑證 近期登入的資訊。

    firebase::Future<firebase::auth::AuthResult> result =
        user->Reauthenticate(credential);
    
  6. 也能使用相同的模式來連結帳戶與 Apple 登入功能。 不過,如果現有的 Firebase 帳戶 已連結到您嘗試連結的 Apple 帳戶。 發生這種情況時,系統傳回的狀態 kAuthErrorCredentialAlreadyInUseAuthResult 可能包含 credential。這個憑證可用於登入與 Apple 連結的 透過 SignInAndRetrieveDataWithCredential 存取帳戶,不需要 產生另一個 Apple 登入權杖和 Nonce。

    firebase::Future<firebase::auth::AuthResult> link_result =
        auth->current_user().LinkWithCredential(credential);
    
    // To keep example simple, wait on the current thread until call completes.
    while (link_result.status() == firebase::kFutureStatusPending) {
      Wait(100);
    }
    
    // Determine the result of the link attempt
    if (link_result.error() == firebase::auth::kAuthErrorNone) {
      // user linked correctly.
    } else if (link_result.error() ==
                   firebase::auth::kAuthErrorCredentialAlreadyInUse &&
               link_result.result()
                   ->additional_user_info.updated_credential.is_valid()) {
      // Sign In with the new credential
      firebase::Future<firebase::auth::AuthResult> result =
          auth->SignInAndRetrieveDataWithCredential(
              link_result.result()->additional_user_info.updated_credential);
    } else {
      // Another link error occurred.
    }
    

Android 版

在 Android 上,整合網頁式 Firebase,透過 Firebase 驗證使用者 使用 Firebase SDK 登入應用程式進行一般 OAuth 登入, 登入流程

如要使用 Firebase SDK 處理登入流程,請按照下列步驟操作:

  1. 建構已設定的 FederatedOAuthProviderData 例項 必須是 Apple 的提供者 ID。

    firebase::auth::FederatedOAuthProviderData provider_data("apple.com");
    
  2. 選用:指定其他 OAuth 2.0 範圍 (預設範圍除外) 要求驗證服務供應商

    provider_data.scopes.push_back("email");
    provider_data.scopes.push_back("name");
    
  3. 選用:如要以特定語言顯示 Apple 的登入畫面 ,請設定 locale 參數。詳情請參閱 使用 Apple 登入說明文件 支援語言代碼

    // Localize to French.
    provider_data.custom_parameters["language"] = "fr";
    ```
    
  4. 設定供應商資料後,請使用該資料建立 FederatedOAuthProvider。

    // Construct a FederatedOAuthProvider for use in Auth methods.
    firebase::auth::FederatedOAuthProvider provider(provider_data);
    
  5. 使用驗證提供者物件向 Firebase 進行驗證。請注意,這不像 其他 FirebaseAuth 作業,這會透過彈出通訊視窗控制您的使用者介面 供使用者輸入憑證的網頁檢視畫面。

    如要啟動登入流程,請呼叫 signInWithProvider

    firebase::Future<firebase::auth::AuthResult> result =
      auth->SignInWithProvider(provider_data);
    

    接著,您的應用程式會等待,或 在 Future 上註冊回呼

  6. 相同的模式可與 ReauthenticateWithProvider 搭配使用 用來為需要修改的敏感作業擷取新憑證 近期登入的資訊。

    firebase::Future<firebase::auth::AuthResult> result =
      user.ReauthenticateWithProvider(provider_data);
    

    接著,您的應用程式會等待或在 未來

  7. 也能使用 LinkWithCredential() 連結不同的識別資訊提供者 。

    請注意,Apple 規定您必須事先取得使用者的明確同意聲明 將 Apple 帳戶連結至其他資料。

    舉例來說,如要將 Facebook 帳戶連結至目前的 Firebase 帳戶,請使用 在使用者登入 Facebook 時取得的存取權杖:

    // Initialize a Facebook credential with a Facebook access token.
    AuthCredential credential =
        firebase::auth::FacebookAuthProvider.getCredential(token);
    
    // Assuming the current user is an Apple user linking a Facebook provider.
    firebase::Future<firebase::auth::AuthResult> result =
        auth.current_user().LinkWithCredential(credential);
    

使用 Apple 記事登入

不同於 Firebase Auth 支援的其他供應商,Apple 不提供 相片網址。

此外,如果使用者選擇不與應用程式分享電子郵件地址,Apple 會為他佈建一個專屬電子郵件地址 (格式為 xyz@privaterelay.appleid.com) 與應用程式共用。如果發生以下情況: 設定私人電子郵件轉發服務後,Apple 會轉寄傳送到 傳送至使用者實際電子郵件地址的匿名地址

Apple 只會將使用者資訊 (例如顯示名稱) 提供給應用程式 使用者初次登入時Firebase 通常會儲存 初次透過 Apple 帳戶登入時,您可在 current_user().display_name()。不過,如果您先前曾使用 Apple 簽署 使用者未透過 Firebase 登入應用程式時,Apple 不會提供 Firebase 的資料 使用者的顯示名稱。

後續步驟

使用者首次登入後,系統會建立新的使用者帳戶 連結至憑證,也就是使用者名稱和密碼、電話號碼 驗證供應商資訊:登入的使用者。系統會將這個新帳戶儲存為 可用來識別各應用程式的使用者 應用程式。

在您的應用程式中,您可以透過 firebase::auth::User 物件。詳情請見 管理使用者

在 Firebase 即時資料庫和 Cloud Storage 安全性規則中,您可以 透過驗證變數中的登入使用者專屬 ID 控制使用者可以存取哪些資料