Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

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

您可以使用Firebase SDK來執行端到端OAuth 2.0登錄流程,從而讓用戶使用其Apple ID向Firebase進行身份驗證。

在你開始之前

要使用Apple登錄用戶,請首先在Apple的開發人員站點上配置“使用Apple登錄”,然後將Apple啟用為Firebase項目的登錄提供程序。

加入蘋果開發者計劃

使用Apple登錄只能由Apple Developer Program的成員配置。

使用Apple配置登錄

Apple Developer網站上,執行以下操作:

  1. 使用“為網絡配置Apple登錄”的第一部分所述,將您的網站與您的應用程序相關聯。出現提示時,將以下URL註冊為返回URL:

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

    您可以在Firebase控制台設置頁面上獲取Firebase項目ID。

    完成後,記下新的服務ID,這將在下一部分中使用。

  2. 使用Apple私鑰創建登錄。在下一部分中,您將需要新的私鑰和密鑰ID。
  3. 如果您使用Firebase Authentication的任何向用戶發送電子郵件的功能,包括電子郵件鏈接登錄,電子郵件地址驗證,帳戶更改吊銷等,請配置Apple私人電子郵件中繼服務並註冊noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (或您的自定義電子郵件模板域),以便Apple可以將Firebase Authentication發送的電子郵件中繼到匿名的Apple電子郵件地址。

啟用Apple作為登錄提供商

  1. 將Firebase添加到您的Android項目。在Firebase控制台中設置應用程序時,請確保註冊應用程序的SHA-1簽名。
  2. Firebase控制台中,打開“身份驗證”部分。在“登錄方法”選項卡上,啟用Apple提供程序。指定您在上一節中創建的服務ID。另外,在OAuth代碼流配置部分中,指定您的Apple Team ID以及您在上一部分中創建的私鑰和密鑰ID。

符合Apple匿名數據要求

使用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. 使用提供者ID為apple.com Builder來構造OAuthProvider的實例:

    爪哇

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

    Kotlin + KTX

    val provider = OAuthProvider.newBuilder("apple.com")
    
  2. 可選:指定其他OAuth 2.0範圍,超出您要向身份驗證提供程序請求的默認範圍。

    爪哇

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

    Kotlin + KTX

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

    默認情況下,啟用每個電子郵件地址一個帳戶後,Firebase會請求電子郵件和名稱範圍。如果將此設置更改為“每個電子郵件地址多個帳戶” ,Firebase不會向Apple請求任何範圍,除非您指定它們。

  3. 可選:如果要以英語以外的其他語言顯示Apple的登錄屏幕,請設置locale參數。有關受支持的語言環境,請參閱“ 使用Apple登錄”

    爪哇

    // 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時立即分離。

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

    要檢查是否有待處理的結果,請調用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");
    }
    

    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()啟動登錄流程:

    爪哇

    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()一起使用,您可以使用該模式來檢索需要最近登錄的敏感操作的新憑據:

爪哇

// 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獲得的訪問令牌:

爪哇

// 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 Sign-In JS SDK處理登錄流,手動構建OAuth流或使用OAuth庫(例如AppAuth)來使用Apple帳戶通過Firebase進行身份驗證。

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

    您可以使用SecureRandom在Android上生成加密安全的隨機數,如以下示例所示:

    爪哇

    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哈希值:

    爪哇

    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令牌,並使用它和未偽隨機數創建AuthCredential

    爪哇

    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進行身份驗證:

    爪哇

    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

爪哇

FirebaseAuth.getInstance().signOut();

Kotlin + KTX

Firebase.auth.signOut()