Google 致力于为黑人社区推动种族平等。查看具体举措

使用 Apple 和 C++ 进行身份验证

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

通过使用 Firebase SDK 执行端到端 OAuth 2.0 登录流程,您可以让用户使用其 Apple ID 向 Firebase 进行身份验证。

在你开始之前

要使用 Apple 登录用户,请首先在 Apple 的开发者网站上配置 Sign In with Apple,然后将 Apple 启用为您的 Firebase 项目的登录提供商。

加入 Apple 开发者计划

Sign In with Apple 只能由Apple Developer Program的成员配置。

配置使用 Apple 登录

必须在 Firebase 项目中启用并正确配置 Apple 登录。配置因 Android 和 Apple 平台而异。在继续之前,请按照Apple 平台和/或Android指南的“配置 Apple 登录”部分进行操作。

启用 Apple 作为登录提供商

  1. Firebase 控制台中,打开Auth部分。在登录方法选项卡上,启用Apple提供程序。
  2. 配置 Apple 登录提供程序设置:
    1. 如果您仅在 Apple 平台上部署您的应用程序,您可以将服务 ID、Apple 团队 ID、私钥和密钥 ID 字段留空。
    2. 对于 Android 设备的支持:
      1. 将 Firebase 添加到您的 Android 项目中。在 Firebase 控制台中设置应用时,请务必注册应用的 SHA-1 签名。
      2. Firebase 控制台中,打开Auth部分。在登录方法选项卡上,启用Apple提供程序。指定您在上一节中创建的服务 ID。此外,在 OAuth 代码流配置部分中,指定您的 Apple Team ID 以及您在上一部分中创建的私钥和密钥 ID。

遵守 Apple 匿名数据要求

Sign In with Apple 为用户提供了在登录时匿名其数据(包括电子邮件地址)的选项。选择此选项的用户拥有域为privaterelay.appleid.com的电子邮件地址。当您在您的应用程序中使用 Sign In with Apple 时,您必须遵守 Apple 有关这些匿名 Apple ID 的任何适用开发者政策或条款。

这包括在您将任何直接识别个人信息与匿名 Apple ID 相关联之前获得任何必要的用户同意。使用 Firebase 身份验证时,这可能包括以下操作:

  • 将电子邮件地址链接到匿名 Apple ID,反之亦然。
  • 将电话号码链接到匿名 Apple ID,反之亦然
  • 将非匿名社交凭证(Facebook、Google 等)链接到匿名 Apple ID,反之亦然。

上面的列表并不详尽。请参阅开发者帐户成员部分中的 Apple 开发者计划许可协议,以确保您的应用程序符合 Apple 的要求。

访问firebase::auth::Auth

Auth类是所有 API 调用的网关。
  1. 添加 Auth 和 App 头文件:
    #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::App获取firebase::auth::Auth类。 AppAuth之间存在一对一的映射关系。
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
    

使用 Firebase SDK 处理登录流程

使用 Apple 登录的流程因 Apple 和 Android 平台而异。

在 Apple 平台上

通过从您的 C++ 代码调用的 Apple Sign In Objective-C SDK 使用 Firebase 对您的用户进行身份验证。

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

    您将在您的登录请求中发送随机数的 SHA256 哈希,Apple 将在响应中保持不变。 Firebase 通过散列原始随机数并将其与 Apple 传递的值进行比较来验证响应。

  2. 启动 Apple 的登录流程,在您的请求中包括 nonce 的 SHA256 哈希和将处理 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 实现中处理 Apple 的响应。如果登录成功,请使用 Apple 响应中的 ID 令牌和未散列的 nonce 向 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. 使用生成的令牌字符串和原始随机数构建 Firebase 凭据并登录 Firebase。

    firebase::auth::OAuthProvider::GetCredential(
            /*provider_id=*/"apple.com", token, nonce,
            /*access_token=*/nullptr);
    
    firebase::Future<firebase::auth::User*> result =
        auth->SignInWithCredential(credential);
    
  5. 相同的模式可以与Reauthenticate一起使用,它可用于检索需要最近登录的敏感操作的新凭据。

    firebase::Future<firebase::auth::SignInResult> result =
        user->Reauthenticate(credential);
    
  6. 相同的模式可用于将帐户与 Apple Sign In 关联。但是,如果现有 Firebase 帐户已与您尝试关联的 Apple 帐户相关联,您可能会遇到错误。发生这种情况时,future 将返回SignInResult状态,并且kAuthErrorCredentialAlreadyInUse的 UserInfo 对象可能包含有效的updated_credential 。此凭据可用于通过SignInWithCredential登录 Apple 关联帐户,而无需生成另一个 Apple 登录令牌和随机数。

    请注意,此操作必须使用LinkAndRetrieveDataWithCredential才能包含凭据,因为updated_credentialSignInResult.UserInfo对象的成员。

    firebase::Future<firebase::auth::SignInResult> link_result =
        auth->current_user()->LinkAndRetrieveDataWithCredential(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()->info.updated_credential.is_valid()) {
      // Sign In with the new credential
      firebase::Future<firebase::auth::User*> result = auth->SignInWithCredential(
          link_result.result()->info.updated_credential);
    } else {
      // Another link error occurred.
    }
    

在安卓上

在 Android 上,通过使用 Firebase SDK 将基于 Web 的通用 OAuth 登录集成到您的应用程序中,使用 Firebase 对您的用户进行身份验证,以执行端到端的登录流程。

要使用 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参数。有关支持的语言环境,请参阅Sign In with 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. 使用 Auth 提供程序对象向 Firebase 进行身份验证。请注意,与其他 FirebaseAuth 操作不同,这将通过弹出一个用户可以在其中输入其凭据的 Web 视图来控制您的 UI。

    要启动登录流程,请调用signInWithProvider

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

    然后,您的应用程序可能会在 Future 上等待或注册回调

  6. ReauthenticateWithProvider可以使用相同的模式,它可用于检索需要最近登录的敏感操作的新凭据。

    firebase::Future<firebase::auth::SignInResult> result =
      user->ReauthenticateWithProvider(provider_data);
    

    然后,您的应用程序可能会在 Future 上等待或注册回调

  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::SignInResult> result =
        auth.getCurrentUser().linkWithCredential(credential);
    

使用 Apple Notes 登录

与 Firebase Auth 支持的其他提供商不同,Apple 不提供照片 URL。

此外,当用户选择不与应用程序共享电子邮件时,Apple 会为该用户提供一个唯一的电子邮件地址(格式xyz@privaterelay.appleid.com ),并与您的应用程序共享。如果您配置了私人电子邮件中继服务,Apple 会将发送到匿名地址的电子邮件转发到用户的真实电子邮件地址。

Apple 仅在用户首次登录时与应用共享用户信息,例如显示名称。通常,Firebase 会在用户首次使用 Apple 登录时存储显示名称,您可以通过getCurrentUser().getDisplayName()获取。但是,如果您之前使用 Apple 将用户登录到应用程序而不使用 Firebase,Apple 将不会向 Firebase 提供用户的显示名称。

下一步

用户首次登录后,会创建一个新用户帐户并将其链接到凭据(即用户名和密码、电话号码或身份验证提供商信息),即用户登录时使用的凭据。这个新帐户作为 Firebase 项目的一部分存储,可用于在项目中的每个应用中识别用户,无论用户如何登录。

在您的应用程序中,您可以从 firebase::auth::user 对象获取用户的基本个人资料信息。请参阅管理用户

在您的 Firebase 实时数据库和云存储安全规则中,您可以从 auth 变量中获取登录用户的唯一用户 ID,并使用它来控制用户可以访问哪些数据。