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

在 Android 上使用 Apple 進行身份驗證

通過使用 Firebase SDK 執行端到端 OAuth 2.0 登錄流程,您可以讓您的用戶使用他們的 Apple ID 向 Firebase 進行身份驗證。

在你開始之前

要使用 Apple 登錄用戶,請先在 Apple 的開發者網站上配置使用 Apple 登錄,然後啟用 Apple 作為 Firebase 項目的登錄提供程序。

加入 Apple 開發者計劃

登錄與蘋果只能由成員進行配置的蘋果開發者計劃

配置使用 Apple 登錄

蘋果開發者網站,請執行以下操作:

  1. 在第一部分中描述關聯你的網站,你的應用程序的網絡與蘋果公司配置登錄。出現提示時,將以下 URL 註冊為返回 URL:

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

    你可以得到你的火力地堡項目ID火力地堡控制台設置頁面

    完成後,記下您的新服務 ID,您將在下一部分中用到它。

  2. 創建一個登錄與蘋果的私鑰。在下一部分中,您將需要新的私鑰和密鑰 ID。
  3. 如果你使用任何的火力地堡認證的功能,發送電子郵件給用戶,包括電子郵件鏈接登錄,電子郵件地址驗證,賬戶撤銷的變化,和其他人,配置了蘋果私人電子郵件中繼服務和註冊noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您自定義的電子郵件模板域),以便 Apple 可以將 Firebase 身份驗證發送的電子郵件中繼到匿名的 Apple 電子郵件地址。

啟用 Apple 作為登錄提供商

  1. 添加火力地堡到您的Android項目。在 Firebase 控制台中設置應用時,請務必註冊應用的 SHA-1 簽名。
  2. 火力地堡控制台,打開驗證部分。在登錄方法選項卡,使蘋果的供應商。指定您在上一節中創建的服務 ID。此外,在OAuth碼流的配置部分,指定您的蘋果團隊ID和私有密鑰和密鑰ID您在上一節中創建。

遵守 Apple 匿名數據要求

誰選擇了這個選項拍在與蘋果公司為用戶提供了匿名的數據,包括他們的電子郵件地址,在登錄時的選項,用戶必須與該域的電子郵件地址privaterelay.appleid.com 。當您在您的應用程序中使用 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. 構造一個實例OAuthProvider使用它的建造者與提供者ID apple.com

    爪哇

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

    科特林+KTX

    val provider = OAuthProvider.newBuilder("apple.com")
    
  2. 可選:指定其他的OAuth 2.0範圍超出了預設的,你想從身份驗證提供者的要求。

    爪哇

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

    科特林+KTX

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

    默認情況下,啟用每個電子郵件地址帳戶,火力地堡請求電子郵件,姓名等範圍。如果更改此設置每個電子郵件地址多個帳戶,除非你指定它們火力地堡不要求從蘋果的任何範圍。

  3. 可選:如果您想在英語以外的語言顯示蘋果的登錄畫面中,將locale參數。請參閱登錄與蘋果文檔的支持的語言環境。

    爪哇

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

    科特林+KTX

    // Localize the Apple authentication screen in French.
    provider.addCustomParameter("locale", "fr")
    
  4. 使用 OAuth 提供程序對象通過 Firebase 進行身份驗證。請注意,不像其他FirebaseAuth操作,這將打開一個自Chrome標籤把你的UI的控制。因此,不引用在你的活動OnSuccessListenerOnFailureListener你附加為當操作啟動UI他們會立即取下。

    您應該首先檢查您是否已經收到回复。使用此方法登錄會將您的 Activity 置於後台,這意味著它可以在登錄流程中被系統回收。如果發生這種情況,為了確保您不會讓用戶再次嘗試,您應該檢查結果是否已經存在。

    要檢查是否存在未決的結果,調用getPendingAuthResult()

    爪哇

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

    科特林+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()

    爪哇

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

    科特林+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。

    此外,當用戶選擇不與應用程序,蘋果公司規定一個唯一的電子郵件地址的用戶(形式分享他們的電子郵件xyz@privaterelay.appleid.com ,其股您的應用程序)。如果您配置了私人電子郵件中繼服務,Apple 會將發送到匿名地址的電子郵件轉發到用戶的真實電子郵件地址。

    蘋果只股票的用戶信息,如第一次的顯示名稱與應用程序在用戶的跡象。通常情況下,火力地堡商店的顯示名稱的第一次用戶登錄在與蘋果,你可以得到getCurrentUser().getDisplayName()但是,如果您之前使用 Apple 將用戶登錄到應用程序而不使用 Firebase,Apple 將不會向 Firebase 提供用戶的顯示名稱。

重新認證和賬戶關聯

同樣的模式可以被用來startActivityForReauthenticateWithProvider()您可以使用檢索需要最近登錄敏感操作到新的憑據:

爪哇

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

科特林+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 獲得的訪問令牌:

爪哇

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

科特林+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.
        }
      });

高級:手動處理登錄流程

你也可以用火力地堡通過使用蘋果登錄在JS SDK或者處理登錄流程,手工打造的OAuth流或使用OAuth用戶庫,如使用的是Apple帳戶進行身份驗證AppAuth未

  1. 對於每個登錄請求,生成一個隨機字符串——一個“nonce”——你將使用它來確保你獲得的 ID 令牌是專門為響應你的應用程序的身份驗證請求而授予的。這一步對於防止重放攻擊很重要。

    您可以生成與Android加密安全隨機數SecureRandom ,如下面的例子:

    爪哇

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

    科特林+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()
    }
    

    然後,獲取 nonce 的 SHA246 哈希作為十六進製字符串:

    爪哇

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

    科特林+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. 您收到蘋果公司的回應後,您可以通過響應令牌的ID,並用它和散列的隨機數以創建AuthCredential

    爪哇

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

    科特林+KTX

    val credential =  OAuthProvider.newCredentialBuilder("apple.com")
        .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce)
        .build()
    
  4. 使用 Firebase 憑據通過 Firebase 進行身份驗證:

    爪哇

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

    科特林+KTX

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

如果調用signInWithCredential成功,則可以使用getCurrentUser方法來獲取用戶的帳戶數據。

下一步

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

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

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

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

要註銷用戶,請撥打signOut

爪哇

FirebaseAuth.getInstance().signOut();

科特林+KTX

Firebase.auth.signOut()