您可以使用 Firebase SDK 執行端對端 OAuth 2.0 登入流程,讓使用者透過 Apple ID 向 Firebase 驗證身分。
事前準備
如要使用 Apple 登入使用者,請先在 Apple 開發人員網站上設定「透過 Apple 登入」,然後為 Firebase 專案啟用 Apple 做為登入供應商。
加入 Apple 開發人員計畫
只有 Apple 開發人員計畫的成員可以設定「使用 Apple 帳戶登入」。
設定「使用 Apple 登入」功能
您必須在 Firebase 專案中啟用 Apple 登入功能,並正確設定。Android 和 Apple 平台的設定方式不同。請先按照Apple 平台和/或 Android 指南的「設定使用 Apple 登入」一節操作,再繼續進行。啟用 Apple 做為登入供應商
- 在 Firebase 控制台中,依序前往「安全性」>「驗證」。
- 在「登入方式」分頁中,啟用「Apple」登入供應商。
- 設定 Apple 登入提供者設定:
- Apple:如果只在 Apple 平台上部署應用程式,可以將服務 ID、Apple Team ID、私密金鑰和金鑰 ID 欄位留空。
-
Android:完成下列步驟,支援 Android 裝置:
- 將 Firebase 新增至 Android 專案。
-
指定應用程式的 SHA-1 指紋 (如果尚未指定)。
-
在 Firebase 控制台中,依序前往
「設定」 > 「一般」分頁標籤。 - 向下捲動至「您的應用程式」資訊卡,選取您的 Android 應用程式,然後在「SHA 憑證指紋」欄位中新增 SHA-1 指紋。
如要瞭解如何取得應用程式的 SHA 指紋,請參閱「驗證用戶端」一文。
-
在 Firebase 控制台中,依序前往
- 設定 Apple 登入提供者設定:
遵守 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 帳戶登入」的程序會因 Apple 和 Android 平台而異。
在 Apple 平台上
透過 C++ 程式碼叫用的 Apple Sign In 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--; } } } }您會在登入要求中傳送 Nonce 的 SHA256 雜湊,Apple 會在回應中不變地傳遞該雜湊。Firebase 會將原始 Nonce 雜湊處理,並與 Apple 傳遞的值進行比較,藉此驗證回應。
啟動 Apple 的登入流程,包括在要求中加入 Nonce 的 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 Credential 並登入 Firebase。
firebase::auth::OAuthProvider::GetCredential( /*provider_id=*/"apple.com", token, nonce, /*access_token=*/nullptr); firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredential(credential);相同的模式可與
Reauthenticate搭配使用,以便擷取需要最近登入的敏感作業的新憑證。firebase::Future<firebase::auth::AuthResult> result = user->Reauthenticate(credential);您也可以使用相同模式連結 Apple 登入帳戶。不過,如果現有的 Firebase 帳戶已連結至您嘗試連結的 Apple 帳戶,您可能會遇到錯誤。發生這種情況時,Future 會傳回
kAuthErrorCredentialAlreadyInUse狀態,而AuthResult可能包含有效的credential。您可以使用這項憑證,透過SignInAndRetrieveDataWithCredential登入已連結 Apple 的帳戶,不必產生其他 Apple 登入權杖和隨機數。firebase::Future<firebase::auth::AuthResult> link_result = auth->current_user().LinkWithCredential(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() ->additional_user_info.updated_credential.is_valid()) { // Sign In with the new credential firebase::Future<firebase::auth::AuthResult> result = auth->SignInAndRetrieveDataWithCredential( link_result.result()->additional_user_info.updated_credential); } else { // Another link error occurred. }
Android 版
在 Android 上,請使用 Firebase SDK 將網頁型一般 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 作業不同,這項作業會彈出網頁檢視畫面,讓使用者輸入憑證,藉此控管您的 UI。
如要啟動登入流程,請呼叫
signInWithProvider:firebase::Future<firebase::auth::AuthResult> result = auth->SignInWithProvider(provider_data);然後,應用程式可能會等待或在 Future 上 註冊回呼。
相同的模式可與
ReauthenticateWithProvider搭配使用,以便擷取需要最近登入的敏感作業的新憑證。firebase::Future<firebase::auth::AuthResult> 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::AuthResult> result = auth.current_user().LinkWithCredential(credential);
使用 Apple Notes 登入
與 Firebase Auth 支援的其他供應商不同,Apple 不會提供相片網址。
此外,如果使用者選擇不與應用程式分享電子郵件地址,Apple 會為該使用者提供專屬電子郵件地址 (格式為 xyz@privaterelay.appleid.com),並與您的應用程式分享。如果您已設定私人電子郵件轉發服務,Apple 會將傳送至匿名地址的電子郵件轉寄至使用者的實際電子郵件地址。
Apple 只會在使用者首次登入時,與應用程式分享顯示名稱等使用者資訊。通常,Firebase 會在使用者首次透過 Apple 登入時儲存顯示名稱,您可以透過 current_user().display_name() 取得該名稱。不過,如果您先前使用 Apple 登入應用程式,但並未採用 Firebase,Apple 就不會提供使用者的顯示名稱給 Firebase。
後續步驟
使用者首次登入後,系統會建立新的使用者帳戶,並連結至使用者登入時使用的憑證 (即使用者名稱和密碼、電話號碼或驗證供應商資訊)。這個新帳戶會儲存在 Firebase 專案中,可用於識別專案中每個應用程式的使用者,無論使用者登入方式為何。在應用程式中,您可以從 firebase::auth::User 物件取得使用者的基本個人資料資訊。請參閱「管理使用者」。
在 Firebase 即時資料庫和 Cloud Storage 安全性規則中,您可以從 auth 變數取得登入使用者的專屬使用者 ID,並用來控管使用者可存取的資料。