Inicio de sesión de usuarios con Apple en iOS
En este documento se explica cómo usar Identity Platform para añadir la opción Iniciar sesión con Apple a tu aplicación iOS.
Antes de empezar
Crea una aplicación iOS que use Identity Platform.
Únete al Programa para Desarrolladores de Apple.
Configurar tu aplicación con Apple
En el sitio para desarrolladores de Apple:
Habilita la función Iniciar sesión con Apple en tu aplicación.
Si usas Identity Platform para enviar correos a tus usuarios, configura tu proyecto con el servicio de retransmisión de correo privado de Apple con la siguiente dirección de correo:
noreply@project-id.firebaseapp.com
También puedes usar una plantilla de correo personalizada, si tu aplicación tiene una.
Cumplir los requisitos de Apple sobre datos anonimizados
Apple ofrece a los usuarios la opción de anonimizar sus datos, incluida su dirección de correo electrónico. Apple asigna a los usuarios que seleccionan esta opción una dirección de correo ofuscada con el dominio privaterelay.appleid.com
.
Tu aplicación debe cumplir las políticas o los términos para desarrolladores aplicables de Apple en relación con los IDs de Apple anonimizados. Esto incluye obtener el consentimiento del usuario antes de asociar cualquier información personal identificable (IPI) con un ID de Apple anonimizado. Entre las acciones que implican IPI, se incluyen las siguientes:
- Vincular una dirección de correo a un ID de Apple anonimizado o viceversa.
- Vincular un número de teléfono a un ID de Apple anonimizado o viceversa
- Vincular una credencial social no anónima, como Facebook o Google, a un ID de Apple anonimizado, o viceversa.
Para obtener más información, consulta el Acuerdo de Licencia del Programa para Desarrolladores de Apple de tu cuenta de desarrollador de Apple.
Configurar Apple como proveedor
Para configurar Apple como proveedor de identidades, sigue estos pasos:
Ve a la página Proveedores de identidades de la consola de Google Cloud .
Haz clic en Add A Provider (Añadir proveedor).
Selecciona Apple en la lista.
En Plataforma, selecciona iOS.
Introduce el ID de paquete de tu aplicación.
Registre los dominios de su aplicación haciendo clic en Añadir dominio en Dominios autorizados. Para actividades de desarrollo,
localhost
ya está habilitado de forma predeterminada.En Configurar la aplicación, haga clic en iOS. Copia el fragmento en el código de tu aplicación para inicializar el SDK de cliente de Identity Platform.
Haz clic en Guardar.
Inicio de sesión de usuarios con el SDK de cliente
Inicia la sesión del usuario y obtén un token de ID mediante el framework Authentication Services de Apple.
Genera una cadena aleatoria, conocida como nonce, llamando a
SecRandomCopyBytes(_:_:_:)
.El nonce se usa para evitar ataques de repetición. Incluyes el hash SHA-256 de tu nonce en la solicitud de autenticación y Apple lo devuelve sin modificar en la respuesta. A continuación, Identity Platform valida la respuesta comparando el hash original con el valor devuelto por Apple.
Inicia el flujo de inicio de sesión de Apple, incluido el hash SHA-256 del nonce que has creado en el paso anterior y una clase de delegado para gestionar la respuesta de 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; }
Gestiona la respuesta de Apple en tu implementación de
ASAuthorizationControllerDelegate
. Si el inicio de sesión se realiza correctamente, usa el token de ID de la respuesta de Apple con el nonce sin cifrar para autenticarte 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 diferencia de muchos otros proveedores de identidad, Apple no proporciona una URL de foto.
Si un usuario decide no compartir su correo real con tu aplicación, Apple le proporcionará una dirección de correo única para que la comparta. Este correo tiene el siguiente formato:
xyz@privaterelay.appleid.com
. Si has configurado el servicio de retransmisión de correo privado, Apple reenvía los correos enviados a la dirección anonimizada a la dirección de correo real del usuario.
Apple solo comparte información de los usuarios, como los nombres visibles, con las aplicaciones la primera vez que inician sesión. En la mayoría de los casos, Identity Platform almacena estos datos, lo que le permite obtenerlos mediante firebase.auth().currentUser.displayName
en sesiones futuras. Sin embargo, si permitiste que los usuarios iniciaran sesión en tu aplicación con Apple antes de integrar Identity Platform, la información de los usuarios no estará disponible.
Eliminación de cuentas de usuario
Apple requiere que las aplicaciones iOS que permitan crear cuentas también permitan a los usuarios iniciar la eliminación de su cuenta desde la aplicación.
Cuando elimines una cuenta de usuario, debes revocar el token del usuario antes de eliminar su cuenta, así como todos los datos que hayas almacenado en Firestore, Cloud Storage y Firebase Realtime Database. Para obtener más información, consulta el artículo Ofrecer la eliminación de cuentas en tu aplicación de la documentación de asistencia para desarrolladores de Apple.
Como Identity Platform no almacena tokens de usuario cuando se crean usuarios con el inicio de sesión de Apple, debes pedir al usuario que inicie sesión antes de revocar su token y eliminar la cuenta. También puedes almacenar el código de autorización para reutilizarlo durante la revocación del token y así evitar que se le pida al usuario que vuelva a iniciar sesión si ya lo ha hecho con la función de inicio de sesión con Apple.
Para revocar el token de un usuario y eliminar su cuenta, ejecuta lo siguiente:
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){
// ...
}];
}
}];
}
}
}
Siguientes pasos
- Más información sobre los usuarios de Identity Platform
- Permite que los usuarios inicien sesión con otros proveedores de identidades.