Puede permitir que sus usuarios se autentiquen con Firebase usando su ID de Apple mediante el SDK de Firebase para llevar a cabo el flujo de inicio de sesión de OAuth 2.0 de un extremo a otro.
Antes de que empieces
Para que los usuarios inicien sesión con Apple, primero configure Iniciar sesión con Apple en el sitio para desarrolladores de Apple y luego habilite Apple como proveedor de inicio de sesión para su proyecto de Firebase.
Únase al programa de desarrolladores de Apple
Solo los miembros del Programa para desarrolladores de Apple pueden configurar Iniciar sesión con Apple.
Configurar Iniciar sesión con Apple
- Habilite Iniciar sesión con Apple para su aplicación en la página Certificados, identificadores y perfiles del sitio para desarrolladores de Apple.
- Si usa alguna de las funciones de Firebase Authentication que envían correos electrónicos a los usuarios, incluido el inicio de sesión con enlace de correo electrónico, la verificación de dirección de correo electrónico, la revocación de cambio de cuenta y otras, configure el servicio de retransmisión de correo electrónico privado de Apple y registre
noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com
(o su dominio de plantilla de correo electrónico personalizado) para que Apple pueda retransmitir los correos electrónicos enviados por Firebase Authentication a direcciones de correo electrónico anonimizadas de Apple.
Habilitar Apple como proveedor de inicio de sesión
- Agregue Firebase a su proyecto de Apple . Asegúrese de registrar el ID del paquete de su aplicación cuando configure su aplicación en Firebase console.
- En Firebase console , abra la sección Auth . En la pestaña Método de inicio de sesión , habilite el proveedor de Apple . Si solo está utilizando Iniciar sesión con Apple en una aplicación, puede dejar vacíos los campos ID de servicio, ID de equipo de Apple, clave privada e ID de clave.
Cumplir con los requisitos de datos anónimos de Apple
Iniciar sesión con Apple ofrece a los usuarios la opción de anonimizar sus datos, incluida su dirección de correo electrónico, al iniciar sesión. Los usuarios que eligen esta opción tienen direcciones de correo electrónico con el dominio privaterelay.appleid.com
. Cuando usa Iniciar sesión con Apple en su aplicación, debe cumplir con las políticas de desarrollador aplicables o los términos de Apple con respecto a estas ID de Apple anonimizadas.
Esto incluye obtener cualquier consentimiento de usuario requerido antes de asociar cualquier información personal de identificación directa con una ID de Apple anonimizada. Al usar Firebase Authentication, esto puede incluir las siguientes acciones:
- Vincule una dirección de correo electrónico a una ID de Apple anónima o viceversa.
- Vincular un número de teléfono a una ID de Apple anonimizada o viceversa
- Vincule una credencial social no anónima (Facebook, Google, etc.) a una ID de Apple anónima o viceversa.
La lista de arriba no es exhaustiva. Consulte el Acuerdo de licencia del Programa para desarrolladores de Apple en la sección Membresía de su cuenta de desarrollador para asegurarse de que su aplicación cumpla con los requisitos de Apple.
Inicie sesión con Apple y autentíquese con Firebase
Para autenticarse con una cuenta de Apple, primero inicie sesión con el usuario en su cuenta de Apple usando el marco de servicios de AuthenticationServices
de Apple y luego use el token de ID de la respuesta de Apple para crear un objeto Firebase AuthCredential
:
Para cada solicitud de inicio de sesión, genere una cadena aleatoria, un "nonce", que usará para asegurarse de que el token de ID que obtenga se haya otorgado específicamente en respuesta a la solicitud de autenticación de su aplicación. Este paso es importante para evitar ataques de repetición.
Puede generar un nonce criptográficamente seguro con
SecRandomCopyBytes(_:_:_)
, como en el siguiente ejemplo:Rápido
// 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 }
C objetivo
// 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]; }
Enviará el hash SHA256 del nonce con su solicitud de inicio de sesión, que Apple pasará sin cambios en la respuesta. Firebase valida la respuesta aplicando hash al nonce original y comparándolo con el valor pasado por Apple.
Rápido
@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 }
C objetivo
- (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; }
Inicie el flujo de inicio de sesión de Apple, incluyendo en su solicitud el hash SHA256 del nonce y la clase delegada que manejará la respuesta de Apple (vea el siguiente paso):
Rápido
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() }
C objetivo
@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]; }
Maneje la respuesta de Apple en su implementación de
ASAuthorizationControllerDelegate
. Si el inicio de sesión fue exitoso, use el token de ID de la respuesta de Apple con el nonce sin hash para autenticarse con Firebase:Rápido
@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)") } }
C objetivo
- (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 otros proveedores compatibles con Firebase Auth, Apple no proporciona una URL de foto.
Además, cuando el usuario elige no compartir su correo electrónico con la aplicación, Apple proporciona una dirección de correo electrónico única para ese usuario (de la forma xyz@privaterelay.appleid.com
), que comparte con su aplicación. Si configuró el servicio de retransmisión de correo electrónico privado, Apple reenvía los correos electrónicos enviados a la dirección anonimizada a la dirección de correo electrónico real del usuario.
Apple solo comparte información del usuario, como el nombre para mostrar, con las aplicaciones la primera vez que un usuario inicia sesión. Por lo general, Firebase almacena el nombre para mostrar la primera vez que un usuario inicia sesión con Apple, que puede obtener con Auth.auth().currentUser.displayName
. Sin embargo, si anteriormente usó Apple para iniciar la sesión de un usuario en la aplicación sin usar Firebase, Apple no proporcionará a Firebase el nombre para mostrar del usuario.
Reautenticación y vinculación de cuentas
El mismo patrón se puede usar con reauthenticateWithCredential()
, que puede usar para recuperar una credencial nueva para operaciones confidenciales que requieren un inicio de sesión reciente:
Rápido
// 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.
// ...
}
C objetivo
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.
// ...
}];
Y puede usar linkWithCredential()
para vincular diferentes proveedores de identidad a cuentas existentes.
Tenga en cuenta que Apple requiere que obtenga el consentimiento explícito de los usuarios antes de vincular sus cuentas de Apple a otros datos.
Iniciar sesión con Apple no le permitirá reutilizar una credencial de autenticación para vincularla a una cuenta existente. Si desea vincular una credencial de Iniciar sesión con Apple a otra cuenta, primero debe intentar vincular las cuentas utilizando la credencial anterior de Iniciar sesión con Apple y luego examinar el error que se devolvió para encontrar una nueva credencial. La nueva credencial se ubicará en el diccionario de información de userInfo
del error y se puede acceder a ella a través de la clave AuthErrorUserInfoUpdatedCredentialKey
.
Por ejemplo, para vincular una cuenta de Facebook a la cuenta actual de Firebase, use el token de acceso que obtuvo al iniciar sesión el usuario en Facebook:
Rápido
// 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.
// ...
}
C objetivo
// 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.
// ...
}];
Próximos pasos
Después de que un usuario inicia sesión por primera vez, se crea una nueva cuenta de usuario y se vincula a las credenciales (es decir, el nombre de usuario y la contraseña, el número de teléfono o la información del proveedor de autenticación) con las que el usuario inició sesión. Esta nueva cuenta se almacena como parte de su proyecto de Firebase y se puede usar para identificar a un usuario en cada aplicación de su proyecto, independientemente de cómo inicie sesión el usuario.
En sus aplicaciones, puede obtener la información básica del perfil del
User
del objeto Usuario. Consulte Administrar usuarios .En sus Reglas de seguridad de Firebase Realtime Database y Cloud Storage, puede obtener el ID de usuario único del usuario que inició sesión de la variable
auth
y usarlo para controlar a qué datos puede acceder un usuario.
Puede permitir que los usuarios inicien sesión en su aplicación utilizando varios proveedores de autenticación vinculando las credenciales del proveedor de autenticación a una cuenta de usuario existente.
Para cerrar la sesión de un usuario, llame signOut:
.
Rápido
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
C objetivo
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
También es posible que desee agregar un código de manejo de errores para la gama completa de errores de autenticación. Consulte Manejo de errores .