您可以使用Firebase SDK來執行端到端OAuth 2.0登錄流程,從而讓用戶使用其Apple ID通過Firebase進行身份驗證。
在你開始之前
要使用Apple登錄用戶,請首先在Apple的開發人員站點上配置“使用Apple登錄”,然後將Apple啟用為Firebase項目的登錄提供程序。
加入蘋果開發者計劃
使用Apple登錄只能由Apple Developer Program成員配置。
使用Apple配置登錄
必須在Firebase項目中啟用並正確配置Apple登錄。跨Android和iOS平台的配置有所不同。在繼續之前,請先遵循iOS和/或Android指南中的“使用Apple配置登錄”部分。使Apple成為登錄提供商
- 在Firebase控制台中,打開“身份驗證”部分。在“登錄方法”選項卡上,啟用Apple提供程序。
- 配置Apple登錄提供程序設置:
- 如果僅在iOS上部署應用程序,則可以將服務ID,Apple Team ID,私鑰和密鑰ID字段保留為空。
- 對於Android設備上的支持:
- 將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::auth::Auth
類
Auth
類是所有API調用的網關。- 添加Auth和App頭文件:
#include "firebase/app.h" #include "firebase/auth.h"
- 在初始化代碼中,創建一個
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__)
- 為您的
firebase::App
獲取firebase::auth::Auth
類。App
和Auth
之間存在一對一的映射。firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
使用Firebase SDK處理登錄流程
使用Apple登錄的過程在iOS和Android平台上有所不同。
在iOS上
通過從C ++代碼調用的Apple登錄Objective-C SDK,使用Firebase對用戶進行身份驗證。
對於每個登錄請求,生成一個隨機字符串(一個“ 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傳遞的值進行比較來驗證響應。
啟動Apple的登錄流程,包括在您的請求中包括隨機數的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; }
在您的ASAuthorizationControllerDelegate`實現中處理Apple的響應。如果登錄成功,請使用Apple響應中的ID令牌和未偽造的隨機數向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); } }
使用生成的令牌字符串和原始隨機數來構建Firebase憑證並登錄到Firebase。
firebase::auth::OAuthProvider::GetCredential( /*provider_id=*/"apple.com", token, nonce, /*access_token=*/nullptr); firebase::Future<firebase::auth::User*> result = auth->SignInWithCredential(credential);
同樣的模式可以被使用
Reauthenticate
可用於檢索需要登錄近期敏感操作的新鮮憑證。firebase::Future<firebase::auth::SignInResult> result = user->Reauthenticate(credential);
可以使用相同的模式將帳戶與Apple登錄關聯。但是,當現有Firebase帳戶已鏈接到您要鏈接的Apple帳戶時,您可能會遇到錯誤。如果發生這種情況未來會返回一個狀態
kAuthErrorCredentialAlreadyInUse
和的用戶信息對象SignInResult
可以包含一個有效的updated_credential
。此憑據可用於通過SignInWithCredential
登錄與Apple關聯的帳戶,而無需生成另一個Apple登錄令牌和隨機數。請注意,您必須使用
LinkAndRetrieveDataWithCredential
來使此操作包含憑據,因為updated_credential
是SignInResult.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上
在Android上,通過使用Firebase SDK將基於Web的通用OAuth登錄集成到您的應用中,以使用Firebase對用戶進行身份驗證,以執行端到端登錄流程。
要使用Firebase SDK處理登錄流程,請按照以下步驟操作:
構造一個
FederatedOAuthProviderData
的實例,該實例配置有適用於Apple的提供者ID。firebase::auth::FederatedOAuthProviderData provider_data("apple.com");
可選:指定其他OAuth 2.0範圍,超出您要向身份驗證提供程序請求的默認範圍。
provider_data.scopes.push_back("email"); provider_data.scopes.push_back("name");
可選:如果要以英語以外的其他語言顯示Apple的登錄屏幕,請設置
locale
參數。有關受支持的語言環境,請參閱“ 使用Apple登錄” 。// Localize to French. provider_data.custom_parameters["language"] = "fr"; ```
一旦配置了提供者數據,就可以使用它來創建FederatedOAuthProvider。
// Construct a FederatedOAuthProvider for use in Auth methods. firebase::auth::FederatedOAuthProvider provider(provider_data);
使用Auth提供程序對象向Firebase進行身份驗證。請注意,與其他FirebaseAuth操作不同,這將通過彈出一個用戶可以在其中輸入其憑據的Web視圖來控制您的UI。
要開始登錄流程,請調用
signInWithProvider
:firebase::Future<firebase::auth::SignInResult> result = auth->SignInWithProvider(provider_data);
然後,您的應用程序可以等待或在Future上註冊回調。
可以將同一模式與
ReauthenticateWithProvider
一起使用,該模式可用於檢索需要最近登錄的敏感操作的新憑據。firebase::Future<firebase::auth::SignInResult> result = user->ReauthenticateWithProvider(provider_data);
然後,您的應用程序可以等待或在Future上註冊回調。
並且,您可以使用
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,然後使用它來控制用戶可以訪問哪些數據。