Autentica mediante Apple con C++

Puedes permitir que los usuarios se autentiquen con Firebase mediante su ID de Apple. Para ello, debes usar el SDK de Firebase a fin de completar el flujo de acceso de OAuth 2.0 de extremo a extremo.

Antes de comenzar

Para que los usuarios accedan con Apple, primero configura esta función en el sitio para desarrolladores de Apple y habilita Apple como proveedor de acceso para tu proyecto de Firebase.

Únete al Programa para desarrolladores de Apple

Solo los miembros del Programa para desarrolladores de Apple pueden configurar la función de acceso con Apple.

Configura la función de acceso con Apple

El acceso con Apple debe estar habilitado y configurado correctamente en tu proyecto de Firebase. La configuración varía entre las plataformas iOS y Android. Sigue la sección sobre cómo configurar el acceso con Apple de las guías para iOS o Android antes de continuar.

Habilita Apple como proveedor de acceso

  1. En Firebase console, abre la sección Aut. En la pestaña Método de acceso, habilita el proveedor Apple.
  2. Establece la configuración del proveedor de acceso con Apple:
    1. Si solo implementas tu app en iOS, puedes dejar los campos ID de servicio, ID de equipo de Apple, ID de clave y clave privada vacíos.
    2. Para obtener asistencia en dispositivos Android:
      1. Agrega Firebase al proyecto Android. Cuando configures tu app en Firebase console, asegúrate de registrar su firma SHA-1.
      2. En Firebase console, abre la sección Auth. En la pestaña Método de acceso, habilita el proveedor Apple. Especifica el ID de servicio que creaste en la sección anterior. Además, en la sección de configuración de flujo de código OAuth, especifica tu ID de equipo de Apple junto con la clave privada y el ID de clave que creaste en la sección anterior.

Cumple con los requisitos de datos anonimizados de Apple

La función de acceso con Apple les ofrece a los usuarios la opción de anonimizar sus datos, incluida su dirección de correo electrónico, durante el acceso. Los usuarios que eligen esta opción tienen direcciones de correo electrónico con el dominio privaterelay.appleid.com. Cuando usas el acceso con Apple en tu app, debes tratar estos ID de Apple anonimizados según las políticas para desarrolladores o las condiciones de Apple aplicables.

Esto incluye obtener los consentimientos del usuario correspondientes antes de asociar cualquier información personal que lo identifique directamente con un ID de Apple anonimizado. Cuando usas Firebase Authentication, esto puede incluir las siguientes acciones:

  • Vincular una dirección de correo electrónico 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 de redes sociales no anónima (Facebook, Google, etc.) a un ID de Apple anonimizado o viceversa

Esta lista no es exhaustiva. Consulta el Contrato de licencia del Programa para desarrolladores de Apple en la sección Membresía de tu cuenta de desarrollador a fin de asegurarte de que tu app cumpla con los requisitos de Apple.

Accede a la clase firebase::auth::Auth

La clase Auth es la puerta de enlace para todas las llamadas a la API.
  1. Agrega los archivos de encabezados de autenticación y de app:
    #include "firebase/app.h"
    #include "firebase/auth.h"
    
  2. En tu código de inicialización, crea una clase firebase::App.
    #if defined(__ANDROID__)
      firebase::App* app =
          firebase::App::Create(firebase::AppOptions(), my_jni_env, my_activity);
    #else
      firebase::App* app = firebase::App::Create(firebase::AppOptions());
    #endif  // defined(__ANDROID__)
    
  3. Adquiere la clase firebase::auth::Auth para tu firebase::App. Hay una asignación uno a uno entre App y Auth.
    firebase::auth::Auth* auth = firebase::auth::Auth::GetAuth(app);
    

Maneja el flujo de acceso con el SDK de Firebase

El proceso para acceder con Apple varía según las plataformas iOS y Android.

En iOS

Autentica a tus usuarios con Firebase mediante el SDK de Objective-C del acceso con Apple invocado desde tu código de C++.

  1. Para cada solicitud de acceso, genera una string aleatoria, un “nonce”, que utilizarás a fin de asegurarte de que se otorgó el token de ID que obtienes específicamente en respuesta a la solicitud de autenticación de tu app. Este paso es importante para evitar ataques de repetición.

      - (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--;
            }
          }
        }
      }
    
    

    Enviarás el hash SHA256 del nonce con tu solicitud de acceso, que Apple pasará sin cambios en la respuesta. Para validar la respuesta, Firebase genera un hash de la nonce original y lo compara con el valor que pasó Apple.

  2. Inicia el flujo de acceso de Apple. Para ello, incluye en tu solicitud el hash SHA256 del nonce y la clase delegada que controlará la respuesta de Apple (consulta el siguiente paso):

      - (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;
      }
    
  3. Controla la respuesta de Apple en tu implementación de ASAuthorizationControllerDelegate`. Si el acceso se realizó correctamente, usa el token de ID de la respuesta de Apple con el nonce sin hash para autenticar con Firebase:

      - (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);
          }
        }
    
  4. Usa la string de token resultante y el nonce original para construir una credencial de Firebase y acceder a la plataforma.

    firebase::auth::OAuthProvider::GetCredential(
            /*provider_id=*/"apple.com", token, nonce,
            /*access_token=*/nullptr);
    
    firebase::Future<firebase::auth::User*> result =
        auth->SignInWithCredential(credential);
    
  5. Puedes usar el mismo patrón con Reauthenticate, el cual permite recuperar credenciales nuevas para operaciones sensibles que requieren un acceso reciente.

    firebase::Future<firebase::auth::SignInResult> result =
        user->Reauthenticate(credential);
    
  6. Puedes usar el mismo patrón para vincular una cuenta mediante Iniciar sesión con Apple. Sin embargo, es posible que experimentes un error si intentas vincular una cuenta de Apple que ya tenga asociada otra cuenta de Firebase. En estos casos, el objeto Future mostrará el estado kAuthErrorCredentialAlreadyInUse, y el objeto UserInfo de SignInResult puede contener un updated_credential válido. Puedes usar esta credencial para acceder a la cuenta vinculada de Apple mediante SignInWithCredential sin necesidad de generar otro token ni nonce de Iniciar sesión con Apple.

    Ten en cuenta que debes usar LinkAndRetrieveDataWithCredential para que esta operación contenga la credencial, ya que updated_credential forma parte del objeto SignInResult.UserInfo.

    firebase::Future<firebase::auth::SignInResult> link_result =
        auth->current_user()->LinkAndRetrieveDataWithCredential(credential);
    
    // To keep example simple, wait on the current thread until call completes.
    while (link_result.status() == firebase::kFutureStatusPending) {
      Wait(100);
    }
    
    // Determine the result of the link attempt
    if (link_result.error() == firebase::auth::kAuthErrorNone) {
      // user linked correctly.
    } else if (link_result.error() ==
                   firebase::auth::kAuthErrorCredentialAlreadyInUse &&
               link_result.result()->info.updated_credential.is_valid()) {
      // Sign In with the new credential
      firebase::Future<firebase::auth::User*> result = auth->SignInWithCredential(
          link_result.result()->info.updated_credential);
    } else {
      // Another link error occurred.
    }
    

En Android

En Android, para autenticar a tus usuarios con Firebase, integra el acceso genérico de OAuth basado en la Web a tu app. Para ello, debes usar el SDK de Firebase a fin de llevar a cabo el flujo de acceso de extremo a extremo.

Para controlar el flujo de acceso con el SDK de Firebase, sigue estos pasos:

  1. Crea una instancia de un FederatedOAuthProviderData configurado con el ID de proveedor adecuado para Apple.

    firebase::auth::FederatedOAuthProviderData provider_data("apple.com");
    
  2. Opcional: Especifica alcances de OAuth 2.0 adicionales aparte del valor predeterminado que deseas solicitar al proveedor de autenticación.

    provider_data.scopes.push_back("email");
    provider_data.scopes.push_back("name");
    
  3. Opcional: Si deseas mostrar la pantalla de acceso de Apple en un idioma que no sea inglés, establece el parámetro locale. Consulta la documentación sobre el acceso con Apple para conocer las configuraciones regionales que se admiten.

    // Localize to French.
    provider_data.custom_parameters["language"] = "fr";
    ```
    
  4. Una vez que se hayan configurado los datos de tu proveedor, úsalos para crear un FederatedOAuthProvider.

    // Construct a FederatedOAuthProvider for use in Auth methods.
    firebase::auth::FederatedOAuthProvider provider(provider_data);
    
  5. Autentica con Firebase mediante el objeto del proveedor de Auth. Ten en cuenta que, a diferencia de otras operaciones de FirebaseAuth, esta mostrará una vista web en la que el usuario puede ingresar sus credenciales y tomará el control de tu IU.

    Para iniciar el flujo de acceso, llama a signInWithProvider:

    firebase::Future<firebase::auth::SignInResult> result =
      auth->SignInWithProvider(provider_data);
    

    Es posible que tu aplicación espere o registre una devolución de llamada en Future.

  6. Se puede utilizar el mismo patrón con ReauthenticateWithProvider, que se puede usar a fin de recuperar credenciales nuevas para operaciones sensibles que requieren un acceso reciente.

    firebase::Future<firebase::auth::SignInResult> result =
      user->ReauthenticateWithProvider(provider_data);
    

    Es posible que tu aplicación espere o registre una devolución de llamada en Future.

  7. Además, puedes usar linkWithCredential() para vincular diferentes proveedores de identidad a cuentas existentes.

    Ten en cuenta que Apple requiere que obtengas el consentimiento explícito de los usuarios antes de vincular sus cuentas de Apple a otros datos.

    Por ejemplo, para vincular una cuenta de Facebook a la cuenta actual de Firebase, usa el token de acceso que obtuviste cuando hiciste que el usuario accediera a Facebook:

    // Initialize a Facebook credential with a Facebook access token.
    AuthCredential credential =
        firebase::auth::FacebookAuthProvider.getCredential(token);
    
    // Assuming the current user is an Apple user linking a Facebook provider.
    firebase::Future<firebase::auth::SignInResult> result =
        auth.getCurrentUser().linkWithCredential(credential);
    

Accede con Apple Notes

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 app, Apple aprovisiona una dirección de correo electrónico única para ese usuario (con formato xyz@privaterelay.appleid.com), que comparte con tu app. Si configuraste el servicio privado de retransmisión de correo electrónico, Apple reenvía a la dirección de correo real del usuario los correos electrónicos enviados a la dirección anonimizada.

Apple solo comparte información del usuario como el nombre visible con las apps a las que el usuario accede por primera vez. Por lo general, Firebase almacena el nombre visible la primera vez que un usuario accede con Apple, y puedes obtenerlo con getCurrentUser().getDisplayName(). Sin embargo, si usaste anteriormente Apple para que un usuario acceda a la app sin usar Firebase, Apple no le proporcionará a Firebase el nombre visible del usuario.

Próximos pasos

Cuando un usuario accede por primera vez, se crea una cuenta de usuario nueva y se la vincula con las credenciales (el nombre de usuario y la contraseña, el número de teléfono o la información del proveedor de autenticación) que el usuario utilizó para acceder. Esta cuenta se almacena como parte de tu proyecto de Firebase y se puede usar para identificar a un usuario en todas las apps del proyecto, sin importar cómo acceda.

En las apps, puedes obtener la información básica del perfil del usuario a partir del objeto firebase::auth::user: Consulta Administra usuarios.

En las reglas de seguridad de Firebase Realtime Database y Cloud Storage, puedes obtener el ID del usuario único que accedió a partir de la variable auth y usarlo para controlar a qué datos podrá acceder.