Możesz pozwolić użytkownikom na uwierzytelnianie w Firebase przy użyciu ich Apple ID, korzystając z pakietu SDK Firebase do przeprowadzania kompleksowego procesu logowania OAuth 2.0.
Zanim zaczniesz
Aby zalogować użytkowników za pomocą Apple, najpierw skonfiguruj opcję Zaloguj się przez Apple w witrynie programistów Apple, a następnie włącz firmę Apple jako dostawcę logowania dla swojego projektu Firebase.
Dołącz do programu deweloperskiego Apple
Logowanie za pomocą konta Apple mogą konfigurować wyłącznie członkowie programu Apple Developer Program .
Skonfiguruj logowanie za pomocą Apple
- Włącz opcję Zaloguj się przez Apple dla swojej aplikacji na stronie Certyfikaty, identyfikatory i profile w witrynie dla programistów Apple.
- Powiąż swoją witrynę internetową z aplikacją zgodnie z opisem w pierwszej sekcji Konfigurowanie logowania przy użyciu konta Apple w Internecie . Po wyświetleniu monitu zarejestruj następujący adres URL jako zwrotny adres URL:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
Identyfikator projektu Firebase możesz uzyskać na stronie ustawień konsoli Firebase . Kiedy skończysz, zanotuj swój nowy identyfikator usługi, który będzie Ci potrzebny w następnej sekcji. - Utwórz login przy użyciu klucza prywatnego Apple . W następnej sekcji będziesz potrzebować nowego klucza prywatnego i identyfikatora klucza.
- Jeśli korzystasz z którejkolwiek funkcji uwierzytelniania Firebase, która wysyła wiadomości e-mail do użytkowników, w tym logowania do łącza e-mail, weryfikacji adresu e-mail, unieważniania zmiany konta i innych, skonfiguruj usługę przekazywania prywatnych wiadomości e-mail firmy Apple i zarejestruj się pod adresem
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(lub Twoja dostosowana domena szablonu wiadomości e-mail), aby Apple mógł przekazywać e-maile wysyłane przez uwierzytelnianie Firebase na anonimowe adresy e-mail Apple.
Włącz firmę Apple jako dostawcę logowania
- Dodaj Firebase do swojego projektu Apple . Pamiętaj, aby zarejestrować identyfikator pakietu aplikacji podczas konfigurowania aplikacji w konsoli Firebase.
- W konsoli Firebase otwórz sekcję Uwierzytelnianie . Na karcie Metoda logowania włącz dostawcę Apple . Określ identyfikator usługi utworzony w poprzedniej sekcji. Ponadto w sekcji konfiguracji przepływu kodu OAuth podaj identyfikator zespołu Apple oraz klucz prywatny i identyfikator klucza utworzone w poprzedniej sekcji.
Spełniaj wymagania Apple dotyczące anonimowych danych
Zaloguj się przez Apple daje użytkownikom możliwość anonimizacji swoich danych, w tym adresu e-mail, podczas logowania. Użytkownicy, którzy wybiorą tę opcję, mają adresy e-mail w domenie privaterelay.appleid.com
. Korzystając w aplikacji z funkcji Zaloguj się przez Apple, musisz przestrzegać wszelkich obowiązujących zasad i warunków programistycznych Apple dotyczących tych anonimowych identyfikatorów Apple ID.
Obejmuje to uzyskanie wymaganej zgody użytkownika przed powiązaniem jakichkolwiek danych osobowych bezpośrednio identyfikujących z zanonimizowanym identyfikatorem Apple ID. W przypadku korzystania z uwierzytelniania Firebase może to obejmować następujące działania:
- Połącz adres e-mail z anonimowym identyfikatorem Apple ID i odwrotnie.
- Połącz numer telefonu z anonimowym identyfikatorem Apple ID i odwrotnie
- Połącz nieanonimowe dane uwierzytelniające w mediach społecznościowych (Facebook, Google itp.) z anonimowym identyfikatorem Apple ID i odwrotnie.
Powyższa lista nie jest wyczerpująca. Zapoznaj się z Umową licencyjną programu Apple Developer w sekcji Członkostwo na swoim koncie programisty, aby upewnić się, że Twoja aplikacja spełnia wymagania Apple.
Zaloguj się za pomocą Apple i uwierzytelnij w Firebase
Aby uwierzytelnić się za pomocą konta Apple, najpierw zaloguj użytkownika na jego konto Apple przy użyciu platformy Apple AuthenticationServices
, a następnie użyj tokena identyfikacyjnego z odpowiedzi Apple, aby utworzyć obiekt Firebase AuthCredential
:
Dla każdego żądania logowania wygeneruj losowy ciąg znaków — „nonce” — którego użyjesz, aby upewnić się, że otrzymany token identyfikacyjny został przyznany konkretnie w odpowiedzi na żądanie uwierzytelnienia Twojej aplikacji. Ten krok jest ważny, aby zapobiec atakom poprzez powtórzenie.
Możesz wygenerować kryptograficznie bezpieczną wartość jednorazową za pomocą
SecRandomCopyBytes(_:_:_)
, jak w poniższym przykładzie:Szybki
private func randomNonceString(length: Int = 32) -> String { precondition(length > 0) var randomBytes = [UInt8](repeating: 0, count: length) let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) if errorCode != errSecSuccess { fatalError( "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)" ) } let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") let nonce = randomBytes.map { byte in // Pick a random character from the set, wrapping around if needed. charset[Int(byte) % charset.count] } return String(nonce) }
Cel C
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce - (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--; } } } return [result copy]; }
Wraz z żądaniem logowania wyślesz skrót SHA256 wartości jednorazowej, który Apple przekaże w odpowiedzi bez zmian. Firebase sprawdza odpowiedź, mieszając oryginalną wartość jednorazową i porównując ją z wartością przekazaną przez Apple.
Szybki
@available(iOS 13, *) private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { String(format: "%02x", $0) }.joined() return hashString }
Cel C
- (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; }
Rozpocznij proces logowania Apple, dołączając w swoim żądaniu skrót SHA256 wartości jednorazowej i klasę delegata, która będzie obsługiwać odpowiedź Apple (zobacz następny krok):
Szybki
import CryptoKit // Unhashed nonce. fileprivate var currentNonce: String? @available(iOS 13, *) func startSignInWithAppleFlow() { let nonce = randomNonceString() currentNonce = nonce let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] request.nonce = sha256(nonce) let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() }
Cel C
@import CommonCrypto; - (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]; }
Obsługuj odpowiedź Apple w implementacji
ASAuthorizationControllerDelegate
. Jeśli logowanie się powiodło, użyj tokena identyfikacyjnego z odpowiedzi Apple z niezaszyfrowaną wartością jednorazową, aby uwierzytelnić się w Firebase:Szybki
@available(iOS 13.0, *) extension MainViewController: ASAuthorizationControllerDelegate { func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { guard let nonce = currentNonce else { fatalError("Invalid state: A login callback was received, but no login request was sent.") } guard let appleIDToken = appleIDCredential.identityToken else { print("Unable to fetch identity token") return } guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { print("Unable to serialize token string from data: \(appleIDToken.debugDescription)") return } // Initialize a Firebase credential, including the user's full name. let credential = OAuthProvider.appleCredential(withIDToken: idTokenString, rawNonce: nonce, fullName: appleIDCredential.fullName) // Sign in with Firebase. Auth.auth().signIn(with: credential) { (authResult, error) in if error { // Error. If error.code == .MissingOrInvalidNonce, make sure // you're sending the SHA256-hashed nonce as a hex string with // your request to Apple. print(error.localizedDescription) return } // User is signed in to Firebase with Apple. // ... } } } func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { // Handle error. print("Sign in with Apple errored: \(error)") } }
Cel C
- (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); } // Initialize a Firebase credential, including the user's full name. FIROAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:IDToken rawNonce:self.appleRawNonce fullName:appleIDCredential.fullName]; // Sign in with Firebase. [[FIRAuth auth] signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error != nil) { // Error. If error.code == FIRAuthErrorCodeMissingOrInvalidNonce, // make sure you're sending the SHA256-hashed nonce as a hex string // with your request to Apple. return; } // Sign-in succeeded! }]; } } - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) { NSLog(@"Sign in with Apple errored: %@", error); }
W przeciwieństwie do innych dostawców obsługiwanych przez Firebase Auth, Apple nie udostępnia adresu URL zdjęcia.
Ponadto, jeśli użytkownik zdecyduje się nie udostępniać aplikacji swojego adresu e-mail, Apple przydziela temu użytkownikowi unikalny adres e-mail (w postaci xyz@privaterelay.appleid.com
), który udostępnia Twojej aplikacji. Jeśli skonfigurowałeś usługę przekazywania prywatnych wiadomości e-mail, Apple przekazuje wiadomości e-mail wysłane na anonimowy adres na prawdziwy adres e-mail użytkownika.
Ponowne uwierzytelnienie i połączenie kont
Tego samego wzorca można użyć w przypadku reauthenticateWithCredential()
, którego można użyć do pobrania nowych danych uwierzytelniających dla wrażliwych operacji wymagających niedawnego logowania:
Szybki
// Initialize a fresh Apple credential with Firebase.
let credential = OAuthProvider.credential(
withProviderID: "apple.com",
IDToken: appleIdToken,
rawNonce: rawNonce
)
// Reauthenticate current Apple user with fresh Apple credential.
Auth.auth().currentUser.reauthenticate(with: credential) { (authResult, error) in
guard error != nil else { return }
// Apple user successfully re-authenticated.
// ...
}
Cel C
FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com",
IDToken:appleIdToken,
rawNonce:rawNonce];
[[FIRAuth auth].currentUser
reauthenticateWithCredential:credential
completion:^(FIRAuthDataResult * _Nullable authResult,
NSError * _Nullable error) {
if (error) {
// Handle error.
}
// Apple user successfully re-authenticated.
// ...
}];
Możesz także użyć linkWithCredential()
, aby połączyć różnych dostawców tożsamości z istniejącymi kontami.
Pamiętaj, że Apple wymaga uzyskania wyraźnej zgody użytkowników przed połączeniem ich kont Apple z innymi danymi.
Zalogowanie się za pomocą Apple nie pozwoli na ponowne użycie danych uwierzytelniających w celu połączenia z istniejącym kontem. Jeśli chcesz powiązać dane uwierzytelniające Zaloguj się przez Apple z innym kontem, musisz najpierw spróbować połączyć konta przy użyciu starych danych uwierzytelniających Zaloguj się przez Apple, a następnie sprawdzić zwrócony błąd i znaleźć nowe dane uwierzytelniające. Nowe dane uwierzytelniające będą znajdować się w słowniku userInfo
błędu i będzie można uzyskać do nich dostęp za pomocą klucza AuthErrorUserInfoUpdatedCredentialKey
.
Na przykład, aby połączyć konto na Facebooku z bieżącym kontem Firebase, użyj tokena dostępu otrzymanego podczas logowania użytkownika na Facebooku:
Szybki
// Initialize a Facebook credential with Firebase.
let credential = FacebookAuthProvider.credential(
withAccessToken: AccessToken.current!.tokenString
)
// Assuming the current user is an Apple user linking a Facebook provider.
Auth.auth().currentUser.link(with: credential) { (authResult, error) in
// Facebook credential is linked to the current Apple user.
// The user can now sign in with Facebook or Apple to the same Firebase
// account.
// ...
}
Cel C
// Initialize a Facebook credential with Firebase.
FacebookAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:accessToken];
// Assuming the current user is an Apple user linking a Facebook provider.
[FIRAuth.auth linkWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
// Facebook credential is linked to the current Apple user.
// The user can now sign in with Facebook or Apple to the same Firebase
// account.
// ...
}];
Cofnięcie tokenu
Firma Apple wymaga, aby aplikacje obsługujące tworzenie kont umożliwiały użytkownikom zainicjowanie usunięcia konta w aplikacji, zgodnie z opisem w Wytycznych dotyczących przeglądu App Store
Aby spełnić ten wymóg, wykonaj następujące kroki:
Upewnij się, że wypełniłeś sekcję konfiguracji identyfikatora usług i przepływu kodu OAuth w konfiguracji logowania za pomocą dostawcy Apple, zgodnie z opisem w sekcji Konfigurowanie logowania za pomocą Apple .
Ponieważ Firebase nie przechowuje tokenów użytkownika, gdy użytkownicy są tworzeni za pomocą funkcji Zaloguj się przez Apple, musisz poprosić użytkownika o ponowne zalogowanie się przed unieważnieniem tokena i usunięciem konta.
Szybki
private func deleteCurrentUser() { do { let nonce = try CryptoUtils.randomNonceString() currentNonce = nonce let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] request.nonce = CryptoUtils.sha256(nonce) let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self authorizationController.performRequests() } catch { // In the unlikely case that nonce generation fails, show error view. displayError(error) } }
Uzyskaj kod autoryzacyjny z
ASAuthorizationAppleIDCredential
i użyj go do wywołaniaAuth.auth().revokeToken(withAuthorizationCode:)
w celu unieważnienia tokenów użytkownika.Szybki
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { print("Unable to retrieve AppleIDCredential") return } guard let _ = currentNonce else { fatalError("Invalid state: A login callback was received, but no login request was sent.") } guard let appleAuthCode = appleIDCredential.authorizationCode else { print("Unable to fetch authorization code") return } guard let authCodeString = String(data: appleAuthCode, encoding: .utf8) else { print("Unable to serialize auth code string from data: \(appleAuthCode.debugDescription)") return } Task { do { try await Auth.auth().revokeToken(withAuthorizationCode: authCodeString) try await user?.delete() self.updateUI() } catch { self.displayError(error) } } }
Na koniec usuń konto użytkownika (i wszystkie powiązane dane)
Następne kroki
Gdy użytkownik zaloguje się po raz pierwszy, zostanie utworzone nowe konto użytkownika i powiązane z poświadczeniami — czyli nazwą użytkownika i hasłem, numerem telefonu lub informacjami o dostawcy uwierzytelniania — za pomocą których użytkownik się zalogował. To nowe konto jest przechowywane jako część Twojego projektu Firebase i może służyć do identyfikowania użytkownika w każdej aplikacji w Twoim projekcie, niezależnie od tego, w jaki sposób użytkownik się loguje.
W Twoich aplikacjach możesz uzyskać podstawowe informacje o profilu użytkownika z obiektu
User
. Zobacz Zarządzanie użytkownikami .W regułach bezpieczeństwa bazy danych Firebase Realtime i Cloud Storage możesz uzyskać unikalny identyfikator zalogowanego użytkownika ze zmiennej
auth
i użyć go do kontrolowania, do jakich danych użytkownik może uzyskać dostęp.
Możesz zezwolić użytkownikom na logowanie się do aplikacji przy użyciu wielu dostawców uwierzytelniania, łącząc poświadczenia dostawcy uwierzytelniania z istniejącym kontem użytkownika.
Aby wylogować użytkownika, wywołaj funkcję signOut:
.
Szybki
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
Cel C
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
Możesz także dodać kod obsługi błędów dla pełnego zakresu błędów uwierzytelniania. Zobacz Błędy obsługi .