Możesz zezwolić użytkownikom na uwierzytelnianie w Firebase przy użyciu ich identyfikatora Apple ID przy użyciu pakietu Firebase SDK do przeprowadzania kompleksowego procesu logowania OAuth 2.0.
Zanim zaczniesz
Aby logować użytkowników za pomocą Apple, najpierw skonfiguruj opcję Logowanie się z Apple w witrynie dla programistów Apple, a następnie włącz Apple jako dostawcę logowania dla swojego projektu Firebase.
Dołącz do Programu dla programistów Apple
Logowanie się przez Apple może być skonfigurowane tylko przez członków programu Apple Developer Program .
Skonfiguruj logowanie za pomocą Apple
- Włącz logowanie się przez Apple dla swojej aplikacji na stronie Certyfikaty, identyfikatory i profile w witrynie Apple dla deweloperów.
- Jeśli korzystasz z funkcji uwierzytelniania Firebase, które wysyłają e-maile do użytkowników, w tym logowania za pomocą łącza e-mail, weryfikacji adresu e-mail, cofnięcia zmiany konta i innych, skonfiguruj usługę przekazywania prywatnej poczty e-mail Apple i zarejestruj
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(lub Twoja dostosowana domena szablonu wiadomości e-mail), aby firma Apple mogła przekazywać wiadomości e-mail wysyłane przez uwierzytelnianie Firebase na anonimowe adresy e-mail firmy Apple.
Włącz 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ę Auth . Na karcie Metoda logowania włącz dostawcę Apple . Jeśli w aplikacji korzystasz tylko z usługi Zaloguj się przez Apple, możesz pozostawić puste pola Identyfikator usługi, Identyfikator zespołu Apple, klucz prywatny i Identyfikator klucza.
Przestrzegaj wymagań Apple dotyczących danych anonimowych
Funkcja Zaloguj się z Apple umożliwia użytkownikom anonimizację danych, w tym adresu e-mail, podczas logowania. Użytkownicy, którzy wybiorą tę opcję, mają adresy e-mail w domenie privaterelay.appleid.com
. Gdy używasz Logowania się przez Apple w swojej aplikacji, musisz przestrzegać wszelkich obowiązujących zasad deweloperskich lub warunków Apple dotyczących tych anonimowych identyfikatorów Apple ID.
Obejmuje to uzyskanie wszelkiej wymaganej zgody użytkownika przed powiązaniem jakichkolwiek bezpośrednio identyfikujących danych osobowych z anonimowym 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 lub odwrotnie.
- Połącz numer telefonu z anonimowym identyfikatorem Apple ID lub odwrotnie
- Połącz nieanonimowe dane uwierzytelniające z serwisów społecznościowych (Facebook, Google itp.) z anonimowym identyfikatorem Apple ID lub odwrotnie.
Powyższa lista nie jest wyczerpująca. Zapoznaj się z Umową licencyjną Apple Developer Program 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 za pomocą Firebase
Aby uwierzytelnić się za pomocą konta Apple, najpierw zaloguj użytkownika na swoje konto Apple przy użyciu struktury Apple AuthenticationServices
, a następnie użyj tokena identyfikatora z odpowiedzi Apple, aby utworzyć obiekt AuthCredential
:
Dla każdego żądania logowania wygeneruj losowy ciąg — „nonce” — którego użyjesz, aby upewnić się, że otrzymany token identyfikatora został przyznany specjalnie w odpowiedzi na żądanie uwierzytelnienia Twojej aplikacji. Ten krok jest ważny, aby zapobiec atakom powtórkowym.
Możesz wygenerować kryptograficznie bezpieczny identyfikator jednorazowy za pomocą
SecRandomCopyBytes(_:_:_)
, jak w poniższym przykładzie:Szybki
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce private func randomNonceString(length: Int = 32) -> String { precondition(length > 0) let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") var result = "" var remainingLength = length while remainingLength > 0 { let randoms: [UInt8] = (0 ..< 16).map { _ in var random: UInt8 = 0 let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) if errorCode != errSecSuccess { fatalError( "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)" ) } return random } randoms.forEach { random in if remainingLength == 0 { return } if random < charset.count { result.append(charset[Int(random)]) remainingLength -= 1 } } } return result }
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 identyfikatora jednorazowego, który Apple przekaże niezmieniony w odpowiedzi. Firebase weryfikuje odpowiedź, mieszając oryginalny identyfikator jednorazowy i porównując go 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, uwzględniając w swoim żądaniu skrót SHA256 wartości jednorazowej i klasę delegata, która będzie obsługiwać odpowiedź firmy 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 swojej implementacji
ASAuthorizationControllerDelegate
. Jeśli logowanie się powiodło, użyj tokena identyfikatora z odpowiedzi Apple z niehaszowaną 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. let credential = OAuthProvider.credential(withProviderID: "apple.com", IDToken: idTokenString, rawNonce: nonce) // 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. FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com" IDToken:idToken rawNonce:rawNonce]; // 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, gdy użytkownik zdecyduje się nie udostępniać aplikacji swojego adresu e-mail, firma Apple przydziela mu unikalny adres e-mail (w postaci xyz@privaterelay.appleid.com
), który udostępnia 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.
Apple udostępnia aplikacjom informacje o użytkowniku, takie jak nazwa wyświetlana, tylko przy pierwszym logowaniu użytkownika. Zwykle Firebase przechowuje nazwę wyświetlaną przy pierwszym logowaniu użytkownika w Apple, którą można uzyskać za pomocą Auth.auth().currentUser.displayName
. Jeśli jednak wcześniej używałeś Apple do logowania użytkownika w aplikacji bez korzystania z Firebase, Apple nie udostępni Firebase nazwy wyświetlanej użytkownika.
Ponowne uwierzytelnianie i łączenie kont
Tego samego wzorca można użyć z reauthenticateWithCredential()
, której można użyć do pobrania nowych poświadczeń dla poufnych 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()
do powiązania różnych dostawców tożsamości z istniejącymi kontami.
Pamiętaj, że firma Apple wymaga uzyskania wyraźnej zgody użytkowników przed połączeniem ich kont Apple z innymi danymi.
Zalogowanie się z Apple nie pozwoli na ponowne użycie poświadczeń uwierzytelniających w celu połączenia z istniejącym kontem. Jeśli chcesz powiązać poświadczenia logowania przez Apple z innym kontem, musisz najpierw spróbować połączyć konta przy użyciu starego poświadczenia logowania przez Apple, a następnie sprawdzić zwrócony błąd, aby znaleźć nowe poświadczenia. Nowe dane uwierzytelniające zostaną umieszczone w słowniku userInfo
błędu i 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 po zalogowaniu 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.
// ...
}];
Następne kroki
Gdy użytkownik zaloguje się po raz pierwszy, tworzone jest nowe konto użytkownika, które jest łączone z poświadczeniami — czyli nazwą użytkownika i hasłem, numerem telefonu lub informacjami o dostawcy autoryzacji — za pomocą których użytkownik się logował. To nowe konto jest przechowywane jako część Twojego projektu Firebase i może służyć do identyfikacji użytkownika w każdej aplikacji w Twoim projekcie, niezależnie od tego, jak użytkownik się loguje.
W swoich aplikacjach możesz uzyskać podstawowe informacje o profilu użytkownika z obiektu
User
. Zobacz Zarządzanie użytkownikami .W regułach bezpieczeństwa Firebase Realtime Database 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 ma dostęp.
Możesz zezwolić użytkownikom na logowanie się do Twojej 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 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 Obsługa błędów .