Autenticati utilizzando Apple

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Puoi consentire ai tuoi utenti di eseguire l'autenticazione con Firebase utilizzando il loro ID Apple utilizzando l'SDK Firebase per eseguire il flusso di accesso OAuth 2.0 end-to-end.

Prima di iniziare

Per eseguire l'accesso degli utenti utilizzando Apple, configurare innanzitutto Accedi con Apple sul sito per sviluppatori di Apple, quindi abilitare Apple come provider di accesso per il progetto Firebase.

Partecipa all'Apple Developer Program

Accedi con Apple può essere configurato solo dai membri dell'Apple Developer Program .

Configura Accedi con Apple

  1. Abilita Accedi con Apple per la tua app nella pagina Certificati, identificatori e profili del sito per sviluppatori di Apple.
  2. Se utilizzi una delle funzionalità di Firebase Authentication che inviano email agli utenti, tra cui l'accesso al collegamento email, la verifica dell'indirizzo email, la revoca della modifica dell'account e altro, configura il servizio di inoltro email privato di Apple e registra noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (o il dominio del tuo modello di email personalizzato) in modo che Apple possa inoltrare le email inviate da Firebase Authentication a indirizzi email Apple anonimi.

Abilita Apple come provider di accesso

  1. Aggiungi Firebase al tuo progetto Apple . Assicurati di registrare l'ID pacchetto dell'app quando configuri l'app nella console Firebase.
  2. Nella console Firebase , apri la sezione Auth . Nella scheda Metodo di accesso abilitare il provider Apple . Se utilizzi solo Accedi con Apple in un'app, puoi lasciare vuoti i campi ID servizio, ID team Apple, chiave privata e ID chiave.

Rispetta i requisiti dei dati anonimi di Apple

Accedi con Apple offre agli utenti la possibilità di rendere anonimi i propri dati, incluso l'indirizzo e-mail, al momento dell'accesso. Gli utenti che scelgono questa opzione hanno indirizzi e-mail con il dominio privaterelay.appleid.com . Quando utilizzi Accedi con Apple nella tua app, devi rispettare tutte le politiche o i termini applicabili per gli sviluppatori di Apple in merito a questi ID Apple anonimizzati.

Ciò include l'ottenimento di qualsiasi consenso dell'utente richiesto prima di associare qualsiasi informazione personale di identificazione diretta con un ID Apple anonimizzato. Quando si utilizza l'autenticazione Firebase, ciò può includere le seguenti azioni:

  • Collega un indirizzo e-mail a un ID Apple anonimizzato o viceversa.
  • Collega un numero di telefono a un ID Apple anonimo o viceversa
  • Collega una credenziale social non anonima (Facebook, Google, ecc.) a un ID Apple anonimizzato o viceversa.

L'elenco di cui sopra non è esaustivo. Fai riferimento al Contratto di licenza dell'Apple Developer Program nella sezione Iscrizione del tuo account sviluppatore per assicurarti che la tua app soddisfi i requisiti di Apple.

Accedi con Apple e autenticati con Firebase

Per eseguire l'autenticazione con un account Apple, prima accedi all'utente al proprio account Apple utilizzando il framework AuthenticationServices di Apple, quindi utilizza il token ID dalla risposta di Apple per creare un oggetto Firebase AuthCredential :

  1. Per ogni richiesta di accesso, genera una stringa casuale, un "nonce", che utilizzerai per assicurarti che il token ID che ottieni sia stato concesso specificamente in risposta alla richiesta di autenticazione della tua app. Questo passaggio è importante per prevenire gli attacchi di riproduzione.

    Puoi generare un nonce crittograficamente sicuro con SecRandomCopyBytes(_:_:_) , come nell'esempio seguente:

    Rapido

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

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

    Invierai l'hash SHA256 del nonce con la tua richiesta di accesso, che Apple passerà senza modifiche nella risposta. Firebase convalida la risposta eseguendo l'hashing del nonce originale e confrontandolo con il valore trasmesso da Apple.

    Rapido

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

    Obiettivo-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. Avvia il flusso di accesso di Apple, includendo nella tua richiesta l'hash SHA256 del nonce e la classe delegata che gestirà la risposta di Apple (vedi il passaggio successivo):

    Rapido

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

    Obiettivo-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. Gestisci la risposta di Apple nella tua implementazione di ASAuthorizationControllerDelegate . Se l'accesso è andato a buon fine, utilizza il token ID dalla risposta di Apple con il nonce senza hash per eseguire l'autenticazione con Firebase:

    Rapido

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

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

A differenza di altri provider supportati da Firebase Auth, Apple non fornisce un URL della foto.

Inoltre, quando l'utente sceglie di non condividere la propria e-mail con l'app, Apple fornisce un indirizzo e-mail univoco per quell'utente (nel formato xyz@privaterelay.appleid.com ), che condivide con l'app. Se hai configurato il servizio di inoltro e-mail privato, Apple inoltra le e-mail inviate all'indirizzo anonimo all'indirizzo e-mail reale dell'utente.

Apple condivide solo le informazioni dell'utente come il nome visualizzato con le app la prima volta che un utente accede. Di solito, Firebase memorizza il nome visualizzato la prima volta che un utente accede con Apple, che puoi ottenere con Auth.auth().currentUser.displayName . Tuttavia, se in precedenza hai utilizzato Apple per far accedere un utente all'app senza utilizzare Firebase, Apple non fornirà a Firebase il nome visualizzato dell'utente.

Riautenticazione e collegamento dell'account

Lo stesso modello può essere utilizzato con reauthenticateWithCredential() , che puoi utilizzare per recuperare una nuova credenziale per operazioni sensibili che richiedono un accesso recente:

Rapido

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

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

Inoltre, puoi utilizzare linkWithCredential() per collegare diversi provider di identità agli account esistenti.

Tieni presente che Apple richiede di ottenere il consenso esplicito degli utenti prima di collegare i loro account Apple ad altri dati.

L'accesso con Apple non ti consentirà di riutilizzare una credenziale di autenticazione per collegarti a un account esistente. Se desideri collegare una credenziale Accedi con Apple a un altro account, devi prima tentare di collegare gli account utilizzando la vecchia credenziale Accedi con Apple, quindi esaminare l'errore restituito per trovare una nuova credenziale. La nuova credenziale si troverà nel dizionario userInfo dell'errore e sarà accessibile tramite la chiave AuthErrorUserInfoUpdatedCredentialKey .

Ad esempio, per collegare un account Facebook all'account Firebase corrente, utilizza il token di accesso ottenuto dall'accesso dell'utente a Facebook:

Rapido

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

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

Prossimi passi

Dopo che un utente ha effettuato l'accesso per la prima volta, viene creato un nuovo account utente e collegato alle credenziali, ovvero nome utente e password, numero di telefono o informazioni sul provider di autenticazione, con cui l'utente ha effettuato l'accesso. Questo nuovo account viene archiviato come parte del tuo progetto Firebase e può essere utilizzato per identificare un utente in ogni app del tuo progetto, indipendentemente da come l'utente accede.

  • Nelle tue app puoi ottenere le informazioni di base sul profilo User dall'oggetto Utente. Vedere Gestire gli utenti .

  • In Firebase Realtime Database and Cloud Storage Security Rules , puoi ottenere l'ID utente univoco dell'utente che ha eseguito l'accesso dalla variabile auth e utilizzarlo per controllare a quali dati può accedere un utente.

Puoi consentire agli utenti di accedere alla tua app utilizzando più provider di autenticazione collegando le credenziali del provider di autenticazione a un account utente esistente.

Per disconnettere un utente, chiama signOut: .

Rapido

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

Obiettivo-C

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

Potresti anche voler aggiungere il codice di gestione degli errori per l'intera gamma di errori di autenticazione. Vedere Gestione degli errori .