Puoi consentire agli utenti di eseguire l'autenticazione con Firebase utilizzando il loro ID Apple: utilizzando l'SDK Firebase per eseguire il flusso di accesso end-to-end OAuth 2.0.
Prima di iniziare
Per eseguire l'accesso degli utenti utilizzando Apple, devi prima configurare Accedi con Apple sul sito per sviluppatori di Apple, quindi attiva Apple come provider di accesso per il tuo progetto Firebase.
Partecipa all'Apple Developer Program
La funzionalità Accedi con Apple può essere configurata solo dai membri di Apple Developer Google Cloud.
Configurare Accedi con Apple
- Attiva la funzionalità Accedi con Apple per la tua app sul Certificati, identificatori e Profili del sito per sviluppatori di Apple.
- Associa il tuo sito web alla tua app come descritto nella prima sezione di Configurare Accedi con Apple per il web. Quando richiesto, registra il seguente URL come URL di ritorno:
Puoi ottenere l'ID progetto Firebase nella pagina delle impostazioni della console Firebase. Al termine, prendi nota del tuo nuovo ID servizio, che dovrai inserire nella prossima sezione.https://
YOUR_FIREBASE_PROJECT_ID .firebaseapp.com/__/auth/handler - Crea un Accedi con la chiave privata Apple. Nella sezione successiva avrai bisogno della nuova chiave privata e dell'ID chiave.
- Se utilizzi una delle funzionalità di Firebase Authentication che inviano email agli utenti, tra cui l'accesso con link email, la verifica dell'indirizzo email, la revoca della modifica dell'account e altre, configura il servizio di inoltro email privato di Apple e registra
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com
(o il dominio del modello email personalizzato) in modo che Apple possa inoltrare le email inviate da Firebase Authentication ad indirizzi email Apple anonimizzati.
Abilita Apple come provider di accesso
- Aggiungi Firebase al tuo progetto Apple. Assicurati di Registra l'ID pacchetto dell'app quando configuri l'app nella Console Firebase.
- Nella console Firebase, apri la sezione Auth. Nella scheda Metodo di accesso, attiva il provider Apple. Specifica l'ID servizio che hai creato nella sezione precedente. Inoltre, nella sezione di configurazione del flusso di codice OAuth, specifica il tuo ID team Apple e la chiave privata e l'ID chiave che hai creato nella sezione precedente.
Rispettare i requisiti di Apple relativi ai dati anonimizzati
Accedi con Apple offre agli utenti la possibilità di anonimizzare i propri dati, incluso l'indirizzo email, quando accedono. Utenti che scelgono questa opzione
hanno indirizzi email con il dominio privaterelay.appleid.com
. Quando
utilizzi Accedi con Apple nella tua app, devi rispettare tutte le
le norme per gli sviluppatori o i termini di Apple relativi a queste norme
ID.
Ciò include l'ottenimento del consenso richiesto dagli utenti prima di associare qualsiasi informazione personale che consenta l'identificazione diretta a un indirizzo Apple anonimizzato. ID. Quando utilizzi Firebase Authentication, potresti includere quanto segue: azioni:
- Collega un indirizzo email a un ID Apple anonimizzato o viceversa.
- Collegare un numero di telefono a un ID Apple anonimizzato o viceversa
- Collegare una credenziale social non anonima (Facebook, Google e così via) a un ID Apple anonimizzato o viceversa.
L'elenco riportato sopra non è esaustivo. Consulta il Contratto di licenza del Programma sviluppatori Apple nella sezione Abbonamento del tuo account sviluppatore per assicurarti che la tua app soddisfi i requisiti di Apple.
Accedi con Apple e autenticati con Firebase
Per autenticarti con un account Apple, fai prima accedere l'utente al suo account Apple utilizzando il framework AuthenticationServices
di Apple, quindi utilizza il token ID della risposta di Apple per creare un oggetto AuthCredential
di Firebase:
Per ogni richiesta di accesso, genera una stringa casuale: "nonce", che utilizzerai per assicurarti che il token ID che ricevi sia concessi specificamente in risposta alla richiesta di autenticazione della tua app. Questo passaggio è importante per prevenire gli attacchi di replay.
Puoi generare un nonce crittograficamente sicuro con
SecRandomCopyBytes(_:_:_)
, come nell'esempio seguente: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) }
// 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 richiesta di accesso, che Apple trasmetterà lo stesso elemento invariato nella risposta. Firebase convalida la risposta eseguendo l'hashing del nonce originale e confrontandolo con il valore trasmesso da Apple.
@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 }
- (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; }
Avvia il flusso di accesso di Apple, incluso nella tua richiesta l'hash SHA256 di il nonce e la classe delegato che gestirà la risposta di Apple (vedi nel passaggio successivo):
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() }
@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]; }
Gestisci la risposta di Apple nell'implementazione
ASAuthorizationControllerDelegate
. Se l'accesso è andato a buon fine, utilizza il token ID della risposta di Apple con il nonce non sottoposto ad hashing per autenticarti con Firebase:@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)") } }
- (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); }
A differenza di altri provider supportati da Firebase Auth, Apple non fornisce un URL della foto.
Inoltre, se l'utente sceglie di non condividere la propria email con l'app,
esegue il provisioning di un indirizzo email univoco per l'utente (nel formato
xyz@privaterelay.appleid.com
), che condivide con la tua app. Se
configurato il servizio di inoltro email privato, Apple inoltra le email inviate al
indirizzo anonimo all'indirizzo email reale dell'utente.
Riautenticazione e collegamento dell'account
Lo stesso pattern può essere utilizzato con reauthenticateWithCredential()
, che puoi
utilizzare per recuperare una nuova credenziale per operazioni sensibili che richiedono
un accesso recente:
// 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.
// ...
}
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 del collegamento i propri account Apple ad altri dati.
Accedi con Apple non ti consente di riutilizzare una credenziale di autenticazione per collegarti a un account esistente. Se vuoi collegare una credenziale Accedi con Apple a un'altra credenziale di Accedi con Apple
devi prima provare a collegare gli account utilizzando la versione precedente di Accedi con
la credenziale Apple ed esamina l'errore restituito per trovare una nuova credenziale.
La nuova credenziale verrà posizionata nel dizionario userInfo
dell'errore e può
accessibile tramite la chiave AuthErrorUserInfoUpdatedCredentialKey
.
Ad esempio, per collegare un account Facebook all'account Firebase corrente, utilizza la token di accesso ottenuto per l'accesso dell'utente a Facebook:
// 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.
// ...
}
// 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.
// ...
}];
Revoca del token
Apple richiede che le app che supportano la creazione di account debbano consentire agli utenti di avviare l'eliminazione del proprio account all'interno dell'app, come descritto nelle linee guida per la revisione dell'App Store.
Per soddisfare questo requisito, implementa i seguenti passaggi:
Assicurati di aver compilato l'ID servizio e la configurazione del flusso di codice OAuth. della configurazione del provider Accedi con Apple, come descritto nell' Configura la funzionalità Accedi con Apple.
Poiché Firebase non archivia i token utente quando gli utenti vengono creati con Accedi con Apple, devi chiedere all'utente di accedere di nuovo prima di revocare ed eliminare l'account.
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) } }
Ottieni il codice di autorizzazione dal
ASAuthorizationAppleIDCredential
e utilizzalo per chiamareAuth.auth().revokeToken(withAuthorizationCode:)
e revocare dei token dell'utente.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) } } }
Infine, elimina l'account utente (e tutti i dati associati).
Passaggi successivi
Dopo che un utente ha eseguito 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 del fornitore di autenticazione, con cui l'utente ha eseguito l'accesso. Questo nuovo viene archiviato come parte del progetto Firebase e può essere utilizzato per identificare a un utente in ogni app del progetto, a prescindere da come esegue l'accesso.
-
Nelle tue app, puoi recuperare le informazioni di base del profilo dell'utente dall'oggetto
User
. Vedi Gestire gli utenti. In Firebase Realtime Database e Cloud Storage Regole di sicurezza, puoi ottieni l'ID utente unico dell'utente che ha eseguito l'accesso dalla variabile
auth
e usarli per controllare i dati a cui un utente può accedere.
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:
.
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
Ti consigliamo inoltre di aggiungere il codice di gestione degli errori per l'intera gamma di errori di autenticazione. Consulta la sezione Gestire gli errori.