Autentica mediante Apple con JavaScript

Puedes permitir que los usuarios se autentiquen con Firebase mediante su ID de Apple usando el SDK de Firebase para 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 la función Iniciar sesión con Apple 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 Iniciar sesión con Apple.

Configura la función de acceso con Apple

En el sitio de desarrolladores de Apple, haz lo siguiente:

  1. Asocia tu sitio web a tu app como se describe en la primera sección de la página sobre cómo configurar el acceso con Apple para la Web. Registra la siguiente URL como una URL de retorno cuando se te solicite que lo hagas:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    Puedes obtener el ID de tu proyecto de Firebase en la página de configuración de Firebase console.

    Cuando hayas terminado, toma nota de tu ID de servicio nuevo, ya que lo necesitarás en la siguiente sección.

  2. Crea una clave privada para acceder con Apple. Necesitarás tu ID de clave y tu clave privada nueva en la siguiente sección.
  3. Si usas alguna de las funciones de Firebase Authentication que envía correos electrónicos a los usuarios (como el acceso con vínculo por correo electrónico, la verificación de dirección de correo electrónico o la revocación de cambio de cuenta, entre otras), configura el servicio privado de retransmisión de correo electrónico de Apple y registra noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (o tu dominio de plantilla de correo electrónico personalizado) para que Apple pueda transmitir los correos electrónicos que envía Firebase Authentication a direcciones de correo electrónico anonimizadas de Apple.

Habilita Apple como proveedor de acceso

  1. Agrega Firebase a tu proyecto.
  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 Iniciar sesión 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 Iniciar sesión con Apple en tu app, debes tratar estos IDs 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, es posible que se incluyan 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 para asegurarte de que tu app cumpla con los requisitos de Apple.

Maneja el flujo de acceso con el SDK de Firebase

Si estás compilando una app web, la manera más sencilla de autenticar a tus usuarios con Firebase mediante sus cuentas de Apple es controlar todo el flujo de acceso con el SDK de Firebase JavaScript.

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

  1. Crea una instancia de un OAuthProvider mediante el ID de proveedor apple.com correspondiente.

    Web

    import { OAuthProvider } from "firebase/auth";
    
    const provider = new OAuthProvider('apple.com');

    Web

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. Opcional: Especifica permisos de OAuth 2.0 adicionales aparte del valor predeterminado que deseas solicitar al proveedor de autenticación.

    Web

    provider.addScope('email');
    provider.addScope('name');

    Web

    provider.addScope('email');
    provider.addScope('name');

    De manera predeterminada, cuando habilitas Una cuenta por dirección de correo electrónico, Firebase solicita los permisos de nombre y correo electrónico. Si cambias esta configuración a Varias cuentas por dirección de correo electrónico, Firebase no solicita ningún permiso de Apple, a menos que lo especifiques.

  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.

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. Autentica con Firebase usando el objeto del proveedor de OAuth. Puedes pedirles a los usuarios que accedan con sus cuentas de Apple a través de una ventana emergente o mediante el redireccionamiento a la página de acceso. En dispositivos móviles, se prefiere el método de redireccionamiento.

    • Para acceder con una ventana emergente, llama a signInWithPopup():

      Web

      import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      signInWithPopup(auth, provider)
        .then((result) => {
          // The signed-in user info.
          const user = result.user;
      
          // Apple credential
          const credential = OAuthProvider.credentialFromResult(result);
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // The credential that was used.
          const credential = OAuthProvider.credentialFromError(error);
      
          // ...
        });

      Web

      firebase
        .auth()
        .signInWithPopup(provider)
        .then((result) => {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
      
          // The signed-in user info.
          var user = result.user;
      
          // You can also get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
        // ...
        })
        .catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
      
          // ...
        });
    • Para acceder mediante el redireccionamiento a la página de acceso, llama a signInWithRedirect() como se indica a continuación:

    Web

    import { getAuth, signInWithRedirect } from "firebase/auth";
    
    const auth = getAuth();
    signInWithRedirect(auth, provider);

    Web

    firebase.auth().signInWithRedirect(provider);

    Después de que el usuario termine de acceder y vuelva a la página, puedes llamar a getRedirectResult() para obtener el resultado:

    Web

    import { getAuth, getRedirectResult, OAuthProvider } from "firebase/auth";
    
    // Result from Redirect auth flow.
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        const credential = OAuthProvider.credentialFromResult(result);
        if (credential) {
          // You can also get the Apple OAuth Access and ID Tokens.
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
        }
        // The signed-in user info.
        const user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The credential that was used.
        const credential = OAuthProvider.credentialFromError(error);
    
        // ...
      });

    Web

    // Result from Redirect auth flow.
    firebase
      .auth()
      .getRedirectResult()
      .then((result) => {
        if (result.credential) {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
    
          // You can get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
    
          // IdP data available in result.additionalUserInfo.profile.
          // ...
        }
        // The signed-in user info.
        var user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
    
        // ...
      });

    Aquí también puedes detectar y resolver errores. Para ver una lista de los códigos de error, consulta la referencia de la API.

    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 firebase.auth().currentUser.displayName. 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.

Reautenticación y vinculación de cuentas

Se puede usar el mismo patrón con reauthenticateWithPopup() y reauthenticateWithRedirect(), que puedes emplear a fin de recuperar una credencial nueva para operaciones confidenciales que requieren un acceso reciente:

Web

import { getAuth, reauthenticateWithPopup, OAuthProvider } from "firebase/auth";

// Result from Redirect auth flow.
const auth = getAuth();
const provider = new OAuthProvider('apple.com');

reauthenticateWithPopup(auth.currentUser, provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.

    // The signed-in user info.
    const user = result.user;

    // You can also get the Apple OAuth Access and ID Tokens.
    const credential = OAuthProvider.credentialFromResult(result);
    const accessToken = credential.accessToken;
    const idToken = credential.idToken;

    // ...
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });

Web

const provider = new firebase.auth.OAuthProvider('apple.com');

firebase
  .auth()
  .currentUser
  .reauthenticateWithPopup(provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.
    /** @type {firebase.auth.OAuthCredential} */
    var credential = result.credential;

    // The signed-in user info.
    var user = result.user;
     // You can also get the Apple OAuth Access and ID Tokens.
    var accessToken = credential.accessToken;
    var idToken = credential.idToken;

    // IdP data available in result.additionalUserInfo.profile.
      // ...
  })
  .catch((error) => {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // The email of the user's account used.
    var email = error.email;
    // The firebase.auth.AuthCredential type that was used.
    var credential = error.credential;

    // ...
  });

Además, puedes usar linkWithPopup() y linkWithRedirect() 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:

Web

import { getAuth, linkWithPopup, FacebookAuthProvider } from "firebase/auth";

const auth = getAuth();
const provider = new FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
linkWithPopup(auth.currentUser, provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // ...

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Web

const provider = new firebase.auth.FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
firebase.auth().currentUser.linkWithPopup(provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // Facebook additional data available in result.additionalUserInfo.profile,

      // Additional Facebook OAuth access token can also be retrieved.
      // result.credential.accessToken

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Autentica con Firebase en una extensión de Chrome

Si desarrollas una app de extensión de Chrome, consulta la guía de Documentos fuera de pantalla.

Ten en cuenta que de todas formas debes verificar el dominio personalizado con Apple de manera similar al dominio predeterminado de firebaseapp.com:

http://auth.custom.example.com/.well-known/apple-developer-domain-association.txt

Revocación de tokens

Apple exige que las apps que admiten la creación de cuentas permitan que los usuarios inicien la eliminación de su cuenta dentro de la app, como se describe en las Pautas de revisión de App Store.

Para cumplir con este requisito, implementa los siguientes pasos:

  1. Asegúrate de completar la sección ID de servicios y la configuración de flujo de código OAuth de la configuración del proveedor de acceso con Apple, como se describe en la sección Configura el inicio de sesión con Apple.

  2. Debido a que Firebase no almacena tokens de usuario cuando los usuarios se crean con la función de acceso con Apple, debes solicitar al usuario que vuelva a acceder antes de revocar su token y borrar la cuenta.

    Luego, obtén el token de acceso de OAuth de Apple de OAuthCredential y úsalo para llamar a revokeAccessToken(auth, token) y revocar el token de acceso de OAuth de Apple.

    const provider = new OAuthProvider('apple.com');
    provider.addScope('email');
    provider.addScope('name');
    
    const auth = getAuth();
    signInWithPopup(auth, provider).then(result => {
      // Get the Apple OAuth access token.
      const credential = OAuthProvider.credentialFromResult(result);
      const accessToken = credential.accessToken;
    
      // Revoke the Apple OAuth access token.
      revokeAccessToken(auth, accessToken)
        .then(() => {
          // Token revoked.
    
          // Delete the user account.
          // ...
        })
        .catch(error => {
          // An error happened.
          // ...
        });
    });
    
  3. Por último, borra la cuenta de usuario (y todos los datos asociados).

Avanzado: Autenticación con Firebase en Node.js

Para autenticar con Firebase en una aplicación de Node.js, haz lo siguiente:

  1. Haz que el usuario acceda con su cuenta de Apple y obtén su token de ID de Apple. Hay varias maneras de hacer esto. Por ejemplo, si tu app de Node.js tiene un frontend de navegador, haz lo siguiente:

    1. En tu backend, genera una string aleatoria (una "nonce") y procesa su hash SHA256. La nonce es un valor de un solo uso que utilizas para validar un único recorrido de ida y vuelta entre tu backend y los servidores de autenticación de Apple.

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = (length) => {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        let nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = function(length) {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        var nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');
    2. En tu página de acceso, especifica la nonce con hash en la configuración del Inicio de sesión con Apple:

      <script src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
      <div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div>
      <script>
          AppleID.auth.init({
              clientId: YOUR_APPLE_CLIENT_ID,
              scope: 'name email',
              redirectURI: URL_TO_YOUR_REDIRECT_HANDLER,  // See the next step.
              state: '[STATE]',  // Optional value that Apple will send back to you
                                 // so you can return users to the same context after
                                 // they sign in.
              nonce: HASHED_NONCE  // The hashed nonce you generated in the previous step.
          });
      </script>
      
    3. Obtén el token del ID de Apple de la respuesta de autenticación POSTed en el servidor:

      app.post('/redirect', (req, res) => {
        const savedState = req.cookies.__session;
        const code = req.body.code;
        const state = req.body.state;
        const appleIdToken = req.body.id_token;
        if (savedState !== state || !code) {
          res.status(403).send('403: Permission denied');
        } else {
          // Sign in with Firebase using appleIdToken. (See next step).
        }
      });
      

    Consulta también cómo configurar tu página web para el acceso con Apple.

  2. Después de obtener el token del ID de Apple del usuario, úsalo para crear un objeto de credencial y haz que el usuario acceda con él:

    Web

    import { getAuth, signInWithCredential, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    
    // Build Firebase credential with the Apple ID token.
    const provider = new OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    signInWithCredential(auth, authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

    Web

    // Build Firebase credential with the Apple ID token.
    const provider = new firebase.auth.OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    firebase.auth().signInWithCredential(authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

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 nueva 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 tus apps, para conocer el estado de autenticación del usuario, te recomendamos configurar un observador en el objeto Auth. Luego podrás obtener la información de perfil básica del usuario a partir del objeto User. Consulta Administra usuarios en Firebase.

  • En tus 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.

Para permitir que los usuarios accedan a tu app mediante varios proveedores de autenticación, puedes vincular las credenciales de estos proveedores con una cuenta de usuario existente.

Para salir de la sesión de un usuario, llama a signOut de la siguiente manera:

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});