Uwierzytelnij przez Apple

Aby zezwolić użytkownikom na uwierzytelnianie w Firebase za pomocą ich Apple ID: używając pakietu SDK Firebase do przeprowadzenia pełnego logowania OAuth 2.0.

Zanim zaczniesz

Aby zalogować użytkowników przy użyciu urządzenia Apple, najpierw skonfiguruj funkcję Zaloguj się przez Apple w witrynie Apple dla deweloperów, a potem włącz Apple jako dostawcę logowania projekt Firebase.

Dołącz do programu dla deweloperów Apple

Funkcja Zaloguj się przez Apple może skonfigurować tylko członkowie Apple Developer Program.

Skonfiguruj logowanie się przez Apple

  1. Włącz funkcję Zaloguj się przez Apple w swojej aplikacji na Certyfikaty, identyfikatory Profile w witrynie Apple dla deweloperów.
  2. Powiąż witrynę z aplikacją w sposób opisany w pierwszej sekcji artykułu Konfigurowanie logowania przez Apple w przeglądarce. Gdy pojawi się odpowiedni komunikat, zarejestruj następujący adres URL jako powrotny adres URL:
    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
    Identyfikator projektu Firebase znajdziesz na stronie ustawień konsoli Firebase. Gdy skończysz, zapisz nowy identyfikator usługi, który będzie Ci potrzebny w przejdź do następnej sekcji.
  3. Utwórz Zaloguj się za pomocą klucza prywatnego Apple. Potrzebujesz nowego klucza prywatnego i klucza Identyfikator znajdziesz w następnej sekcji.
  4. Jeśli używasz dowolnej funkcji Firebase Authentication, która wysyła e-maile do użytkowników, w tym logowanie za pomocą linku e-mail, weryfikacja adresu e-mail, zmiana konta unieważnienie w innych, skonfigurować usługę Apple Private Mail relay i zarejestruj się noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (lub dostosowanej domeny szablonu e-maila), dzięki czemu Apple może przekazywać wysyłane e-maile. od Firebase Authentication na zanonimizowane adresy e-mail Apple.

Włącz Apple jako dostawcę logowania

  1. Dodaj Firebase do projektu Apple. Koniecznie zarejestruj identyfikator pakietu aplikacji podczas konfigurowania aplikacji w Konsola Firebase.
  2. W konsoli Firebase otwórz sekcję Uwierzytelnianie. Na karcie Metoda logowania: włącz dostawcę Apple. Podaj identyfikator usługi utworzony w poprzedniej sekcji. Ponadto w Sekcja konfiguracji przepływu kodu OAuth, podaj identyfikator zespołu Apple oraz klucz prywatny i identyfikator klucza utworzone w poprzedniej sekcji.

Zgodność z wymaganiami firmy Apple dotyczącymi zanonimizowanych danych

Zaloguj się przez Apple umożliwia anonimizację danych użytkowników, łącznie z jego adresem e-mail. Użytkownicy, którzy wybiorą tę opcję mają adresy e-mail w domenie privaterelay.appleid.com. Kiedy korzystasz w aplikacji z funkcji Zaloguj się przez Apple, musisz przestrzegać wszystkich obowiązujących zasady dla deweloperów lub warunki firmy Apple dotyczące tych zanonimizowanych Identyfikatory.

Obejmuje to uzyskanie wymaganej zgody użytkownika przed kojarzyć żadnych danych osobowych bezpośrednio umożliwiających identyfikację ze zanonimizowanymi danymi firmy Apple ID. Gdy korzystasz z Uwierzytelniania Firebase, może to obejmować: czynności:

  • Połącz adres e-mail z anonimowym identyfikatorem Apple ID lub odwrotnie.
  • Łączenie numeru telefonu z anonimowym identyfikatorem Apple ID i odwrotnie
  • Połącz nieanonimowe dane społecznościowe (Facebook, Google itp.) z lub na odwrót.

Powyższa lista nie jest wyczerpująca. Zapoznaj się z informacjami o programie dla deweloperów Apple Umowę licencyjną w sekcji Członkostwo na koncie dewelopera, aby upewnić się, że Twoja aplikacja spełnia wymagania Apple.

Zaloguj się przez Apple i uwierzytelnij się w Firebase

Aby uwierzytelnić się za pomocą konta Apple, najpierw zaloguj użytkownika na jego koncie Apple z kontem firmy Apple AuthenticationServices, a następnie użyj tokena identyfikatora z odpowiedzi Apple do utworzenia Firebase Obiekt AuthCredential:

  1. Dla każdego żądania logowania wygeneruj losowy ciąg znaków, „nonce” – służy do sprawdzenia, czy otrzymany token przyznanych w odpowiedzi na żądanie uwierzytelnienia aplikacji. Ten jest ważny, aby zapobiec atakom typu „replay”.

    Możesz wygenerować kryptograficznie zabezpieczoną liczbę jednorazową za pomocą SecRandomCopyBytes(_:_:_) jak w tym przykładzie:

    Swift

    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)
    }
    
        

    Objective-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];
    }
        

    W żądaniu logowania wysyłasz hasz SHA256 identyfikatora jednorazowego, który Apple przekazuje w odpowiedzi niezmienioną treść. Firebase weryfikuje odpowiedź. przez zaszyfrowanie oryginalnej liczby jednorazowej i porównanie jej z wartością przekazaną przez Apple.

    Swift

    @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
    }
    
        

    Objective-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;
    }
        
  2. Uruchom proces logowania Apple, uwzględnij w żądaniu identyfikator SHA256 liczbę jednorazową i klasę delegata, która będzie obsługiwać odpowiedź Apple (zobacz następny krok):

    Swift

    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()
    }
    

    Objective-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];
    }
    
  3. Obsłuż odpowiedź Apple w swojej implementacji ASAuthorizationControllerDelegate Jeśli udało Ci się zalogować, użyj identyfikatora token z odpowiedzi Apple z niezaszyfrowaną liczbą jednorazową do uwierzytelnienia Firebase:

    Swift

    @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)")
      }
    
    }
    

    Objective-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 Uwierzytelnienie Firebase Apple nie udostępnia adresu URL zdjęcia.

Ponadto, jeśli użytkownik zdecyduje się nie udostępniać aplikacji e-mail, Apple udostępnia danemu użytkownikowi niepowtarzalny adres e-mail (w formie formularza xyz@privaterelay.appleid.com), które udostępnia Twojej aplikacji. Jeśli jeśli skonfigurujesz usługę prywatnej usługi przekaźnika poczty e-mail, Apple przekazuje e-maile wysyłane na anonimowy adres e-mail użytkownika.

Ponowne uwierzytelnianie i łączenie kont

Tego samego wzoru można użyć z zasadą reauthenticateWithCredential(), którą może służyć do pobierania nowych danych logowania w przypadku operacji poufnych, które wymagają ostatnie logowanie:

Swift

// 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.
  // ...
}

Objective-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.
  // ...
}];

Za pomocą linkWithCredential() możesz też łączyć różnych dostawców tożsamości z istniejących kont.

Pamiętaj, że firma Apple wymaga od użytkowników uzyskania wyraźnej zgody przed połączeniem swoje konta Apple do innych danych.

Funkcja Zaloguj się przez Apple nie będzie zezwalać na ponowne użycie danych uwierzytelniających w celu połączenia z istniejącego konta. Jeśli chcesz połączyć dane logowania w ramach funkcji Zaloguj się przez Apple z innym konta, musisz najpierw spróbować połączyć konta, korzystając ze starego konta Zaloguj się przez dane logowania Apple, a następnie sprawdź zwrócony błąd, aby znaleźć nowe dane logowania. Nowe dane logowania będą znajdować się w słowniku userInfo błędu i mogą być można uzyskać za pomocą klawisza AuthErrorUserInfoUpdatedCredentialKey.

Aby na przykład połączyć konto na Facebooku z bieżącym kontem Firebase, użyj funkcji token dostępu uzyskany po zalogowaniu użytkownika na Facebooku:

Swift

// 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.
  // ...
}

Objective-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.
  // ...
}];

Unieważnienie tokena

Apple wymaga, aby aplikacje umożliwiające tworzenie kont mogły inicjować działania użytkowników usunięcia konta w aplikacji, zgodnie z opisem w Informacjach na temat App Store Wytyczne

Aby spełnić to wymaganie, wykonaj następujące czynności:

  1. Upewnij się, że zostały wypełnione pola Identyfikator usług i Konfiguracja procesu kodu OAuth. konfiguracji funkcji Zaloguj się przez Apple, jak opisano Skonfiguruj funkcję Zaloguj się przez Apple.

  2. Firebase nie przechowuje tokenów użytkowników tworzonych za pomocą Zaloguj się przez Apple, zanim cofniesz zaproszenie, musisz poprosić użytkownika o ponowne zalogowanie się token i usunięcie konta.

    Swift

    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)
      }
    }
    
    
  3. uzyskać kod autoryzacji ze strony ASAuthorizationAppleIDCredential, użyj go do wywołania Auth.auth().revokeToken(withAuthorizationCode:) w celu unieważnienia tokeny użytkownika.

    Swift

    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)
        }
      }
    }
    
    
  4. Na koniec usuń konto użytkownika (i wszystkie powiązane z nim dane).

Dalsze kroki

Gdy użytkownik zaloguje się po raz pierwszy, tworzone jest nowe konto użytkownika. powiązane z danymi logowania, czyli z nazwą użytkownika, hasłem i numerem telefonu, numer telefonu lub informacje o dostawcy uwierzytelniania – użytkownik zalogowany. Ten nowy jest przechowywane w ramach projektu Firebase i może służyć do identyfikowania użytkownika w każdej aplikacji w projekcie, niezależnie od tego, jak się loguje.

  • W swoich aplikacjach możesz uzyskać podstawowe informacje o profilu użytkownika z User . Zobacz Zarządzanie użytkownikami.

  • Na liście Firebase Realtime Database i Cloud Storage regułami zabezpieczeń, pobierz ze zmiennej auth unikalny identyfikator zalogowanego użytkownika, i używać ich do kontrolowania, do jakich danych użytkownik ma dostęp.

Możesz zezwolić użytkownikom na logowanie się w aplikacji przy użyciu wielokrotnego uwierzytelniania. dostawców, łącząc dane logowania dostawcy uwierzytelniania z istniejącego konta użytkownika.

Aby wylogować użytkownika, wywołaj signOut:

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective-C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

Można także dodać kod obsługi błędów dla pełnego zakresu uwierzytelniania . Patrz Obsługa błędów.