Accesso degli utenti con Apple su iOS
Questo documento mostra come utilizzare Identity Platform per aggiungere Accedi con Apple alla tua app per iOS.
Prima di iniziare
Attiva Identity Platform e disponi di un'app per iOS di base. Per scoprire come attivare Identity Platform ed eseguire l'accesso, consulta la guida rapida.
Partecipa all'Apple Developer Program.
Configurazione dell'app con Apple
Sul sito Apple Developer:
Abilita la funzionalità Accedi con Apple per la tua app.
Se utilizzi Identity Platform per inviare email ai tuoi utenti, configura il tuo progetto con il servizio di inoltro email privato di Apple utilizzando il seguente indirizzo email:
noreply@project-id.firebaseapp.com
Puoi anche utilizzare un modello email personalizzato, se la tua app ne ha uno.
Conformità ai requisiti di Apple per i dati anonimizzati
Apple offre agli utenti la possibilità di anonimizzare i propri dati, incluso
l'indirizzo email. Apple assegna agli utenti che selezionano questa opzione un indirizzo email
offuscato con il dominio privaterelay.appleid.com
.
La tua app deve rispettare tutte le norme o i termini per gli sviluppatori vigenti di Apple relativi agli ID Apple anonimizzati. Ciò include l'ottenimento del consenso dell'utente prima di associare qualsiasi informazione che consente l'identificazione personale (PII) a un Apple ID anonimizzato. Le azioni che coinvolgono PII includono, a titolo esemplificativo:
- Collegamento di un indirizzo email a un Apple ID anonimo o viceversa.
- Collegamento di un numero di telefono a un Apple ID anonimizzato o viceversa
- Collegamento di una credenziale social non anonima, ad esempio Facebook o Google, a un Apple ID anonimizzato o viceversa.
Per ulteriori informazioni, consulta il contratto di licenza del programma per sviluppatori Apple per il tuo account sviluppatore Apple.
Configurazione di Apple come fornitore
Per configurare Apple come provider di identità:
Vai alla pagina Provider di identità nella console Google Cloud .
Fai clic su Aggiungi un provider.
Seleziona Apple dall'elenco.
In Piattaforma, seleziona iOS.
Inserisci l'ID bundle della tua app.
Registra i domini della tua app facendo clic su Aggiungi dominio in Domini autorizzati. A scopo di sviluppo,
localhost
è già attivato per impostazione predefinita.In Configura la tua applicazione, fai clic su iOS. Copia lo snippet nel codice della tua app per inizializzare l'SDK client di Identity Platform.
Fai clic su Salva.
Accesso degli utenti con l'SDK client
Accedi all'utente e ottieni un token ID utilizzando il framework Authentication Services di Apple.
Genera una stringa casuale, nota come nonce, chiamando
SecRandomCopyBytes(_:_:_:)
.Il nonce viene utilizzato per impedire attacchi di replay. Includi l'hash SHA-256 del nonce nella richiesta di autenticazione e Apple lo restituisce, senza modifiche, nella risposta. Identity Platform convalida quindi la risposta confrontando l'hash originale con il valore restituito da Apple.
Avvia il flusso di accesso di Apple, incluso l'hash SHA-256 del nonce che hai creato nel passaggio precedente e una classe delegata per gestire la risposta di Apple:
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() } @available(iOS 13, *) private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { return String(format: "%02x", $0) }.joined() return hashString }
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]; } - (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; }
Gestisci la risposta di Apple nell'implementazione di
ASAuthorizationControllerDelegate
. Se l'accesso va a buon fine, utilizza il token ID dalla risposta di Apple con il nonce non sottoposto ad hashing per l'autenticazione con Identity Platform: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. 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)") } }
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. 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 molti altri provider di identità, Apple non fornisce un URL della foto.
Se un utente sceglie di non condividere il proprio indirizzo email reale con la tua app, Apple fornisce
un indirizzo email univoco da condividere. Questa email ha il formato
xyz@privaterelay.appleid.com
. Se hai configurato il servizio di inoltro email privato, Apple inoltra le email inviate all'indirizzo anonimo all'indirizzo email reale dell'utente.
Apple condivide le informazioni utente, come i nomi visualizzati, con le app solo
la prima volta che un utente esegue l'accesso. Nella maggior parte dei casi, Identity Platform memorizza questi dati, che ti consentono di recuperarli utilizzando firebase.auth().currentUser.displayName
durante le sessioni future. Tuttavia, se hai consentito agli utenti di accedere alla tua app
utilizzando Apple prima dell'integrazione con Identity Platform, le informazioni utente
non sono disponibili.
Eliminazione dell'account utente
Apple richiede che le app per iOS che supportano la creazione di account consentano anche agli utenti di avviare l'eliminazione del proprio account dall'interno dell'app.
Quando elimini un account utente, devi revocare il token dell'utente prima di eliminare l'account utente, nonché tutti i dati che hai archiviato per suo conto in Firestore, Cloud Storage e Firebase Realtime Database. Per ulteriori informazioni, consulta la sezione Offrire l'eliminazione dell'account nella tua app nella documentazione di assistenza per gli sviluppatori di Apple.
Poiché Identity Platform non memorizza i token utente quando gli utenti vengono creati con l'accesso con Apple, devi chiedere all'utente di accedere prima di revocare il token ed eliminare l'account. In alternativa, per evitare di chiedere all'utente di accedere di nuovo se ha eseguito l'accesso con l'accesso con Apple, puoi memorizzare il codice di autorizzazione da riutilizzare durante la revoca del token.
Per revocare il token di un utente ed eliminare il suo account, esegui il comando seguente:
Swift
let user = Auth.auth().currentUser
// Check if the user has a token.
if let providerData = user?.providerData {
for provider in providerData {
guard let provider = provider as? FIRUserInfo else {
continue
}
if provider.providerID() == "apple.com" {
isAppleProviderLinked = true
}
}
}
// Re-authenticate the user and revoke their token
if isAppleProviderLinked {
let request = appleIDRequest(withState: "revokeAppleTokenAndDeleteUser")
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
} else {
// Usual user deletion
}
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
if authorization.credential is ASAuthorizationAppleIDCredential {
let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential
if authorization.credential is ASAuthorizationAppleIDCredential {
if appleIDCredential.state == "signIn" {
// Sign in with Firebase.
// ...
} else if appleIDCredential.state == "revokeAppleTokenAndDeleteUser" {
// Revoke token with Firebase.
Auth.auth().revokeTokenWithAuthorizationCode(code) { error in
if error != nil {
// Token revocation failed.
} else {
// Token revocation succeeded then delete user again.
let user = Auth.auth().currentUser
user?.delete { error in
// ...
}
}
}
}
}
}
}
Objective-C
FIRUser *user = [FIRAuth auth].currentUser;
// Check if the user has a token.
BOOL isAppleProviderLinked = false;
for (id<FIRUserInfo> provider in user.providerData) {
if ([[provider providerID] isEqual:@"apple.com"]) {
isAppleProviderLinked = true;
}
}
// Re-authenticate the user and revoke their token
if (isAppleProviderLinked) {
if (@available(iOS 13, *)) {
ASAuthorizationAppleIDRequest *request =
[self appleIDRequestWithState:@"revokeAppleTokenAndDeleteUser"];
ASAuthorizationController *controller = [[ASAuthorizationController alloc]
initWithAuthorizationRequests:@[ request ]];
controller.delegate = self;
controller.presentationContextProvider = self;
[controller performRequests];
}
} else {
// Usual user deletion
}
- (void)authorizationController:(ASAuthorizationController *)controller
didCompleteWithAuthorization:(ASAuthorization *)authorization
API_AVAILABLE(ios(13.0)) {
if ([authorization.credential
isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
ASAuthorizationAppleIDCredential *appleIDCredential =
authorization.credential;
if ([appleIDCredential.state isEqualToString:@"signIn"]) {
// Sign in with Firebase.
// ...
} else if ([appleIDCredential.state
isEqualToString:@"revokeAppleTokenAndDeleteUser"]) {
// Revoke token with Firebase.
NSString *code =
[[NSString alloc] initWithData:appleIDCredential.authorizationCode
encoding:NSUTF8StringEncoding];
[[FIRAuth auth]
revokeTokenWithAuthorizationCode:code
completion:^(NSError *_Nullable error) {
if (error != nil) {
// Token revocation failed.
} else {
// Token revocation succeeded then delete
// user again.
FIRUser *user = [FIRAuth auth].currentUser;
[user deleteWithCompletion:^(
NSError *_Nullable error){
// ...
}];
}
}];
}
}
}
Passaggi successivi
- Scopri di più sugli utenti di Identity Platform.
- Accedere con altri provider di identità.