Procéder à la connexion des utilisateurs avec Apple sur iOS
Ce document explique comment utiliser Identity Platform pour ajouter Se connecter avec Apple à votre application iOS.
Avant de commencer
Créez une application iOS utilisant Identity Platform.
Rejoignez le programme Apple Developer.
Configurer votre application avec Apple
Sur le site Apple Developer :
Activez la fonctionnalité Se connecter avec Apple pour votre application.
Si vous utilisez Identity Platform pour envoyer des e-mails à vos utilisateurs, configurez votre projet avec le service de relais de messagerie privé d'Apple à l'aide de l'adresse e-mail suivante :
noreply@project-id.firebaseapp.com
Vous pouvez également utiliser un modèle d'e-mail personnalisé, si votre application en possède un.
Conformité avec les exigences de données anonymes d'Apple
Apple offre aux utilisateurs la possibilité d'anonymiser leurs données, y compris leur adresse e-mail. Apple attribue aux utilisateurs qui sélectionnent cette option une adresse e-mail masquée contenant le domaine privaterelay.appleid.com
.
Votre application doit respecter le règlement pour les développeurs ou les conditions d'utilisation d'Apple concernant les identifiants Apple anonymes. Cela inclut l'obtention de l'autorisation de l'utilisateur avant d'associer des informations personnelles à un identifiant Apple anonyme. Les actions impliquant des informations personnelles incluent, sans s'y limiter :
- Associer une adresse e-mail à un identifiant Apple anonyme, ou inversement
- Associer un numéro de téléphone à un identifiant Apple anonyme, ou inversement
- Associer un identifiant de réseau social non anonyme, tel que Facebook ou Google, à un identifiant Apple anonyme, ou inversement
Pour plus d'informations, reportez-vous au Contrat de licence du programme Apple Developer associé à votre compte de développeur Apple.
Configurer Apple en tant que fournisseur
Pour configurer Apple en tant que fournisseur d'identité :
Accédez à la page Fournisseurs d'identité dans la console Google Cloud.
Cliquez sur Ajouter un fournisseur.
Dans la liste, sélectionnez Apple.
Sous Plate-forme, sélectionnez iOS.
Saisissez l'ID de votre application.
Enregistrez les domaines de votre application en cliquant sur Ajouter un domaine sous Domaines autorisés. À des fins de développement,
localhost
est déjà activé par défaut.Sous Configurer votre application, cliquez sur iOS. Copiez l'extrait dans le code de votre application pour initialiser le SDK client Identity Platform.
Cliquez sur Enregistrer.
Connecter des utilisateurs avec le SDK client
Procédez à la connexion de l'utilisateur et obtenez un jeton d'identification à l'aide du framework des services d'authentification d'Apple.
Générez une chaîne aléatoire (nonce) en appelant
SecRandomCopyBytes(_:_:_:)
.Cette technique est utilisée pour empêcher les attaques de répétitions. Vous devez inclure le hachage SHA-256 de votre chaîne nonce dans votre requête d'authentification, et Apple la renvoie, non modifiée, dans la réponse. Identity Platform valide ensuite la réponse en comparant le hachage d'origine à la valeur renvoyée par Apple.
Démarrez le flux de connexion d'Apple, y compris le hachage SHA-256 de la chaîne nonce que vous avez créée à l'étape précédente et une classe déléguée pour gérer la réponse d'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; }
Traitez la réponse d'Apple dans votre mise en œuvre de
ASAuthorizationControllerDelegate
. Si la connexion réussit, utilisez le jeton d'ID de la réponse d'Apple avec le non-hachage de la chaîne nonce pour s'authentifier auprès d'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); }
Contrairement à de nombreux autres fournisseurs d'identité, Apple ne fournit pas d'URL de photo.
Si un utilisateur choisit de ne pas partager son véritable adresse e-mail avec votre application, Apple provisionne une adresse e-mail unique à la place. Cette adresse e-mail se présente sous la forme suivante : xyz@privaterelay.appleid.com
. Si vous avez configuré le service de relais de messagerie privé, Apple transfère les e-mails envoyés à l'adresse anonyme à l'adresse e-mail réelle de l'utilisateur.
Apple ne partage des informations utilisateur, telles que les noms à afficher, qu'avec les applications la première fois qu'un utilisateur se connecte. Dans la plupart des cas, Identity Platform stocke ces données, ce qui vous permet de les récupérer à l'aide de firebase.auth().currentUser.displayName
lors des sessions futures. Toutefois, si vous avez autorisé les utilisateurs à se connecter à votre application à l'aide d'Apple avant l'intégration à Identity Platform, les informations sur l'utilisateur ne sont pas disponibles.
Suppression d'un compte utilisateur
Apple exige que les applications iOS compatibles avec la création de compte doit également permettre aux utilisateurs de lancer la suppression de leur compte depuis l'application.
Lorsque vous supprimez un compte utilisateur, vous devez révoquer son jeton avant de supprimer son compte, ainsi que toutes les données que vous avez stockées pour lui dans Firestore, Cloud Storage et Firebase Realtime Database. Pour en savoir plus, consultez la section Offrir la suppression de compte dans votre application dans la documentation d'assistance pour les développeurs d'Apple.
Étant donné qu'Identity Platform ne stocke pas les jetons utilisateur lorsque les utilisateurs sont créés avec la connexion Apple, vous devez demander à l'utilisateur de se connecter avant de révoquer son jeton et de supprimer le compte. Pour éviter de demander à l'utilisateur de se reconnecter s'il est déjà connecté avec la fonctionnalité de connexion Apple, vous pouvez stocker le code d'autorisation pour le réutiliser lors de la révocation du jeton.
Pour révoquer le jeton d'un utilisateur et supprimer son compte, exécutez la commande suivante :
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){
// ...
}];
}
}];
}
}
}
Étape suivante
- En savoir plus sur les utilisateurs d'Identity Platform.
- Procéder à la connexion des utilisateurs avec d'autres fournisseurs d'identité.