您可以使用Firebase SDK來執行端到端OAuth 2.0登錄流程,從而讓用戶使用其Apple ID向Firebase進行身份驗證。
在你開始之前
要使用Apple登錄用戶,請首先在Apple的開發人員站點上配置“使用Apple登錄”,然後將Apple啟用為Firebase項目的登錄提供程序。
加入蘋果開發者計劃
使用Apple登錄只能由Apple Developer Program的成員配置。
使用Apple配置登錄
在Apple Developer網站上,執行以下操作:
如使用“為網絡配置Apple登錄”的第一部分所述,將您的網站與您的應用程序相關聯。出現提示時,將以下URL註冊為返回URL:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
您可以在Firebase控制台設置頁面上獲取Firebase項目ID。
完成後,記下新的服務ID,這將在下一部分中使用。
- 使用Apple私鑰創建登錄。在下一部分中,您將需要新的私鑰和密鑰ID。
如果您使用Firebase Authentication的任何向用戶發送電子郵件的功能,包括電子郵件鏈接登錄,電子郵件地址驗證,帳戶更改吊銷等,請配置Apple私人電子郵件中繼服務並註冊
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(或您的自定義電子郵件模板域),以便Apple可以將Firebase Authentication發送的電子郵件中繼到匿名的Apple電子郵件地址。
啟用Apple作為登錄提供商
- 將Firebase添加到您的Android項目。在Firebase控制台中設置應用程序時,請確保註冊應用程序的SHA-1簽名。
- 在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處理登錄流程,請按照以下步驟操作:
使用提供者ID為
apple.com
Builder來構造OAuthProvider
的實例:爪哇
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");
Kotlin + KTX
val provider = OAuthProvider.newBuilder("apple.com")
可選:指定其他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請求任何範圍,除非您指定它們。
可選:如果要以英語以外的其他語言顯示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")
使用OAuth提供程序對象通過Firebase進行身份驗證。請注意,與其他
FirebaseAuth
操作不同,這將通過打開“自定義Chrome標籤”來控制您的UI。因此,請勿在您附加的OnSuccessListener
和OnFailureListener
中引用您的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進行身份驗證。
對於每個登錄請求,生成一個隨機字符串(“ 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傳遞的值進行比較來驗證響應。
使用OAuth庫或其他方法啟動Apple的登錄流程。請確保在請求中包含散列的隨機數作為參數。
收到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()
使用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()