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

在 Android 上使用 Apple 进行身份验证

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

通过使用 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 登录

Apple Developer网站上,执行以下操作:

  1. 将您的网站与您的应用程序相关联,如Configure Sign In with Apple for web的第一部分所述。出现提示时,将以下 URL 注册为返回 URL:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    您可以在Firebase 控制台设置页面上获取您的 Firebase 项目 ID。

    完成后,记下您的新服务 ID,您将在下一部分中用到它。

  2. 使用 Apple 私钥创建登录。在下一部分中,您将需要新的私钥和密钥 ID。
  3. 如果您使用 Firebase 身份验证的任何功能向用户发送电子邮件,包括电子邮件链接登录、电子邮件地址验证、帐户更改撤销等,请配置 Apple 私人电子邮件中继服务并注册noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您自定义的电子邮件模板域),以便 Apple 可以将 Firebase 身份验证发送的电子邮件中继到匿名的 Apple 电子邮件地址。

启用 Apple 作为登录提供商

  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 SDK 处理登录流程

在 Android 上,使用 Apple 帐户通过 Firebase 对用户进行身份验证的最简单方法是使用 Firebase Android SDK 处理整个登录流程。

要使用 Firebase Android SDK 处理登录流程,请执行以下步骤:

  1. 使用提供者 ID apple.com的 Builder 构造OAuthProvider的实例:

    Java

    OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
    

    Kotlin+KTX

    val provider = OAuthProvider.newBuilder("apple.com")
    
  2. 可选:指定您希望从身份验证提供程序请求的默认值之外的其他 OAuth 2.0 范围。

    Java

    List<String> scopes =
        new ArrayList<String>() {
          {
            add("email");
            add("name");
          }
        };
    provider.setScopes(scopes);
    

    Kotlin+KTX

    provider.setScopes(arrayOf("email", "name"))
    

    默认情况下,启用每个电子邮件地址一个帐户时,Firebase 会请求电子邮件和名称范围。如果您将此设置更改为Multiple accounts per email address ,除非您指定,否则 Firebase 不会向 Apple 请求任何范围。

  3. 可选:如果要以英语以外的语言显示 Apple 的登录屏幕,请设置locale参数。有关支持的语言环境,请参阅Sign In with Apple 文档

    Java

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr");
    

    Kotlin+KTX

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr")
    
  4. 使用 OAuth 提供程序对象向 Firebase 进行身份验证。请注意,与其他FirebaseAuth操作不同,这将通过打开自定义 Chrome 选项卡来控制您的 UI。因此,不要在您附加的OnSuccessListenerOnFailureListener中引用您的 Activity,因为它们会在操作启动 UI 时立即分离。

    您应该首先检查您是否已经收到回复。使用此方法登录会将您的 Activity 置于后台,这意味着它可以在登录流程中被系统回收。为了确保在发生这种情况时不会让用户重试,您应该检查结果是否已经存在。

    要检查是否有待处理的结果,请调用getPendingAuthResult()

    Java

    mAuth = FirebaseAuth.getInstance();
    Task<AuthResult> pending = mAuth.getPendingAuthResult();
    if (pending != null) {
        pending.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
            @Override
            public void onSuccess(AuthResult authResult) {
                Log.d(TAG, "checkPending:onSuccess:" + authResult);
                // Get the user profile with authResult.getUser() and
                // authResult.getAdditionalUserInfo(), and the ID
                // token from Apple with authResult.getCredential().
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "checkPending:onFailure", e);
            }
        });
    } else {
        Log.d(TAG, "pending: null");
    }
    

    Kotlin+KTX

    val pending = auth.pendingAuthResult
    if (pending != null) {
        pending.addOnSuccessListener { authResult ->
            Log.d(TAG, "checkPending:onSuccess:$authResult")
            // Get the user profile with authResult.getUser() and
            // authResult.getAdditionalUserInfo(), and the ID
            // token from Apple with authResult.getCredential().
        }.addOnFailureListener { e ->
            Log.w(TAG, "checkPending:onFailure", e)
        }
    } else {
        Log.d(TAG, "pending: null")
    }
    

    如果没有待处理的结果,请通过调用startActivityForSignInWithProvider()启动登录流程:

    Java

    mAuth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener(
                    new OnSuccessListener<AuthResult>() {
                        @Override
                        public void onSuccess(AuthResult authResult) {
                            // Sign-in successful!
                            Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser());
                            FirebaseUser user = authResult.getUser();
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.w(TAG, "activitySignIn:onFailure", e);
                        }
                    });
    

    Kotlin+KTX

    auth.startActivityForSignInWithProvider(this, provider.build())
            .addOnSuccessListener { authResult ->
                // Sign-in successful!
                Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}")
                val user = authResult.user
                // ...
            }
            .addOnFailureListener { e ->
                Log.w(TAG, "activitySignIn:onFailure", e)
            }
    

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

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

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

重新认证和帐户链接

startActivityForReauthenticateWithProvider()可以使用相同的模式,您可以使用它来检索需要最近登录的敏感操作的新凭据:

Java

// The user is already signed-in.
FirebaseUser firebaseUser = mAuth.getCurrentUser();

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener(
        new OnSuccessListener<AuthResult>() {
          @Override
          public void onSuccess(AuthResult authResult) {
            // User is re-authenticated with fresh tokens and
            // should be able to perform sensitive operations
            // like account deletion and email or password
            // update.
          }
        })
    .addOnFailureListener(
        new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Handle failure.
          }
        });

Kotlin+KTX

// The user is already signed-in.
val firebaseUser = auth.getCurrentUser()

firebaseUser
    .startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
    .addOnSuccessListener( authResult -> {
        // User is re-authenticated with fresh tokens and
        // should be able to perform sensitive operations
        // like account deletion and email or password
        // update.
    })
    .addOnFailureListener( e -> {
        // Handle failure.
    })

而且,您可以使用linkWithCredential()将不同的身份提供者链接到现有帐户。

请注意,Apple 要求您在将用户的 Apple 帐户链接到其他数据之前获得用户的明确同意。

例如,要将 Facebook 帐户链接到当前 Firebase 帐户,请使用从用户登录 Facebook 时获得的访问令牌:

Java

// Initialize a Facebook credential with a Facebook access token.
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
      @Override
      public void onComplete(@NonNull Task<AuthResult> task) {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      }
    });

Kotlin+KTX

// Initialize a Facebook credential with a Facebook access token.
val credential = FacebookAuthProvider.getCredential(token.getToken())

// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
    .addOnCompleteListener(this, task -> {
        if (task.isSuccessful()) {
          // Facebook credential is linked to the current Apple user.
          // The user can now sign in to the same account
          // with either Apple or Facebook.
        }
      });

高级:手动处理登录流程

您还可以使用 Apple 帐户向 Firebase 进行身份验证,方法是使用 Apple Sign-In JS SDK、手动构建 OAuth 流程或使用 OAuth 库(例如AppAuth )来处理登录流程。

  1. 对于每个登录请求,生成一个随机字符串——一个“nonce”——你将使用它来确保你获得的 ID 令牌是专门为响应你的应用程序的身份验证请求而授予的。此步骤对于防止重放攻击很重要。

    您可以使用SecureRandom在 Android 上生成加密安全随机数,如下例所示:

    Java

    private String generateNonce(int length) {
        SecureRandom generator = new SecureRandom();
    
        CharsetDecoder charsetDecoder = StandardCharsets.US_ASCII.newDecoder();
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE);
    
        byte[] bytes = new byte[length];
        ByteBuffer inBuffer = ByteBuffer.wrap(bytes);
        CharBuffer outBuffer = CharBuffer.allocate(length);
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes);
            inBuffer.rewind();
            charsetDecoder.reset();
            charsetDecoder.decode(inBuffer, outBuffer, false);
        }
        outBuffer.flip();
        return outBuffer.toString();
    }
    

    Kotlin+KTX

    private fun generateNonce(length: Int): String {
        val generator = SecureRandom()
    
        val charsetDecoder = StandardCharsets.US_ASCII.newDecoder()
        charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE)
        charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE)
    
        val bytes = ByteArray(length)
        val inBuffer = ByteBuffer.wrap(bytes)
        val outBuffer = CharBuffer.allocate(length)
        while (outBuffer.hasRemaining()) {
            generator.nextBytes(bytes)
            inBuffer.rewind()
            charsetDecoder.reset()
            charsetDecoder.decode(inBuffer, outBuffer, false)
        }
        outBuffer.flip()
        return outBuffer.toString()
    }
    

    然后,将随机数的 SHA246 哈希作为十六进制字符串:

    Java

    private String sha256(String s) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest(s.getBytes());
        StringBuilder hash = new StringBuilder();
        for (byte c: digest) {
            hash.append(String.format("%02x", c));
        }
        return hash.toString();
    }
    

    Kotlin+KTX

    private fun sha256(s: String): String {
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(s.toByteArray())
        val hash = StringBuilder()
        for (c in digest) {
            hash.append(String.format("%02x", c))
        }
        return hash.toString()
    }
    

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

  2. 使用您的 OAuth 库或其他方法启动 Apple 的登录流程。请务必将散列随机数作为参数包含在您的请求中。

  3. 收到 Apple 的响应后,从响应中获取 ID 令牌并使用它和未散列的 nonce 创建一个AuthCredential

    Java

    AuthCredential credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build();
    

    Kotlin+KTX

    val credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build()
    
  4. 使用 Firebase 凭据向 Firebase 进行身份验证:

    Java

    mAuth.signInWithCredential(credential)
        .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
          @Override
          public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
              // User successfully signed in with Apple ID token.
              // ...
            }
          }
        });
    

    Kotlin+KTX

    auth.signInWithCredential(credential)
          .addOnCompleteListener(this) { task ->
              if (task.isSuccessful) {
                // User successfully signed in with Apple ID token.
                // ...
              }
          }
    

如果signInWithCredential调用成功,您可以使用getCurrentUser方法获取用户的帐户数据。

下一步

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

  • 在您的应用中,您可以从FirebaseUser对象获取用户的基本个人资料信息。请参阅管理用户

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

您可以通过将身份验证提供程序凭据链接到现有用户帐户来允许用户使用多个身份验证提供程序登录您的应用程序。

要注销用户,请调用signOut

Java

FirebaseAuth.getInstance().signOut();

Kotlin+KTX

Firebase.auth.signOut()