Autentica con Firebase mediante un vínculo de correo electrónico en plataformas de Apple

Puedes usar Firebase Authentication para que los usuarios accedan mediante el envío de un correo electrónico con un vínculo en el que se pueda hacer clic. Además, en el proceso se verifica la dirección de correo electrónico del usuario.

Existen numerosos beneficios asociados al acceso por correo electrónico:

  • Es sencillo registrarse y acceder.
  • Hay un menor riesgo de reutilización de contraseñas entre aplicaciones, lo que puede comprometer incluso la seguridad de contraseñas bien seleccionadas.
  • Se puede autenticar un usuario y, al mismo tiempo, verificar que sea el dueño legítimo de la dirección de correo electrónico.
  • El usuario solo necesita una cuenta de correo electrónico con acceso para ingresar. No es necesario poseer un número de teléfono ni una cuenta en redes sociales.
  • El usuario puede acceder sin necesidad de proporcionar (o recordar) una contraseña, lo que resulta especialmente práctico si se utiliza un dispositivo móvil.
  • Un usuario existente que antes accedió con un identificador de correo electrónico (contraseña o federado) puede actualizar la configuración para acceder solamente con el correo electrónico. Por ejemplo, un usuario que olvidó su contraseña puede acceder de igual modo sin necesidad de restablecerla.

Antes de comenzar

Usa Swift Package Manager para instalar y administrar las dependencias de Firebase.

  1. En Xcode, con tu proyecto de app abierto, navega a File > Add Packages.
  2. Cuando se te solicite, agrega el repositorio del SDK de Firebase para plataformas de Apple:
  3.   https://github.com/firebase/firebase-ios-sdk
  4. Elige la biblioteca de Firebase Authentication.
  5. Cuando termines, Xcode comenzará a resolver y descargar automáticamente tus dependencias en segundo plano.

A fin de que los usuarios accedan a través de un vínculo de correo electrónico, primero debes habilitar el método de acceso con proveedor de correo electrónico y con vínculo de correo electrónico para el proyecto de Firebase:

  1. En Firebase console, abre la sección Autenticación.
  2. En la pestaña Método de acceso, habilita el proveedor de Correo electrónico/Contraseña. Ten en cuenta que se debe habilitar el acceso mediante correo electrónico/contraseña para utilizar el acceso con un vínculo de correo electrónico.
  3. En la misma sección, habilita el método de acceso con Vínculo del correo electrónico (acceso sin contraseña).
  4. Haz clic en Guardar.

Para iniciar el proceso de autenticación, muéstrale al usuario una interfaz que le pida ingresar su dirección de correo electrónico y, luego, llama a sendSignInLink para solicitar a Firebase que envíe el vínculo de autenticación al correo electrónico del usuario.

  1. Construye el objeto ActionCodeSettings, que proporciona a Firebase las instrucciones para construir el vínculo de correo electrónico. Configura los siguientes campos:

    • url: Es el vínculo directo que se debe incorporar y cualquier estado adicional que se deba pasar. El dominio del vínculo debe estar en la lista de dominios autorizados de Firebase console, que se encuentra en la pestaña Método de acceso (Authentication > Método de acceso).
    • iOSBundleID y androidPackageName: Son las apps que se deben usar cuando el vínculo de acceso se abre en un dispositivo Android o Apple. Obtén más información para configurar Firebase Dynamic Links a fin de ejecutar acciones en correos electrónicos mediante apps para dispositivos móviles.
    • handleCodeInApp: Se debe configurar como verdadero. A diferencia de otras acciones de correo electrónico fuera de banda (restablecimiento de contraseñas y verificaciones por correo electrónico), la operación de acceso siempre debe completarse en la app. Esto se debe a que, al final del flujo, se espera que el usuario acceda y que su estado de autenticación permanezca en la app.
    • dynamicLinkDomain: Si se definen varios dominios de vínculos dinámicos personalizados para un proyecto, especifica cuál usar cuando el vínculo se abra mediante una app para dispositivos móviles determinada (por ejemplo, example.page.link). De lo contrario, el primer dominio se selecciona automáticamente.

    Swift

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.url = URL(string: "https://www.example.com")
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName("com.example.android",
                                             installIfNotAvailable: false, minimumVersion: "12")
    

    Objective-C

    FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
    [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]];
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = YES;
    [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]];
    [actionCodeSettings setAndroidPackageName:@"com.example.android"
                        installIfNotAvailable:NO
                               minimumVersion:@"12"];
    

    Para obtener más información acerca de ActionCodeSettings, consulta la sección sobre cómo pasar el estado en acciones de correo electrónico.

  2. Pídele el correo electrónico al usuario.

  3. Envía el vínculo de autenticación al correo electrónico del usuario y guarda su correo en caso de que este complete el acceso con correo electrónico en el mismo dispositivo.

    Swift

    Auth.auth().sendSignInLink(toEmail: email,
                               actionCodeSettings: actionCodeSettings) { error in
      // ...
        if let error = error {
          self.showMessagePrompt(error.localizedDescription)
          return
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        UserDefaults.standard.set(email, forKey: "Email")
        self.showMessagePrompt("Check your email for link")
        // ...
    }
    

    Objective-C

    [[FIRAuth auth] sendSignInLinkToEmail:email
                       actionCodeSettings:actionCodeSettings
                               completion:^(NSError *_Nullable error) {
      // ...
        if (error) {
          [self showMessagePrompt:error.localizedDescription];
           return;
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"];
        [self showMessagePrompt:@"Check your email for link"];
        // ...
    }];
    

Medidas de seguridad

Para evitar que un vínculo de acceso se use para acceder como un usuario no deseado o en un dispositivo no deseado, Firebase Auth requiere que se proporcione la dirección de correo electrónico del usuario cuando se complete el proceso de acceso. Para que el acceso sea exitoso, esta dirección de correo electrónico debe coincidir con la dirección a la que se envió originalmente el vínculo de acceso.

Puedes simplificar este flujo para los usuarios que abren el vínculo de acceso en el mismo dispositivo en el que solicitan el vínculo, almacenando su dirección de correo electrónico de manera local al enviar el correo electrónico de acceso. Luego, usa esta dirección para completar el flujo.

Después de finalizar el acceso, cualquier mecanismo de acceso previo sin verificar se eliminará del usuario y se invalidarán las sesiones existentes. Por ejemplo, si alguien creó previamente una cuenta no verificada con el mismo correo electrónico y contraseña, se quitará la contraseña del usuario para evitar que el ladrón de identidad que reclamó la propiedad y creó esa cuenta no verificada vuelva a acceder con ella.

Completa el acceso en una app para dispositivos móviles Apple

Firebase Authentication utiliza Firebase Dynamic Links para enviar el vínculo de correo electrónico a un dispositivo móvil. Para completar el acceso a través de la aplicación para dispositivos móviles, la app debe configurarse para detectar el vínculo entrante, analizar el vínculo directo subyacente y luego completar el acceso.

Firebase Auth utiliza Firebase Dynamic Links cuando envía un vínculo que debe abrirse en una aplicación para dispositivos móviles. Para poder usar esta función, se debe configurar Dynamic Links en Firebase console.

  1. Habilita Firebase Dynamic Links:

    1. En Firebase console, abre la sección Dynamic Links.
    2. Si aún no aceptaste los términos ni creaste un dominio de Dynamic Links, deberás hacerlo ahora.

      Si ya creaste un dominio de Dynamic Links, anótalo. Estos dominios suelen ser como el siguiente ejemplo:

      example.page.link

      Necesitarás este valor cuando configures la app para Apple o Android a fin de interceptar el vínculo entrante.

  2. Configura aplicaciones para Apple:

    1. Si planeas manejar estos vínculos desde tu aplicación, debes especificar el ID del paquete en la configuración del proyecto de Firebase console. Además, es necesario especificar el ID de App Store y el del equipo de desarrolladores de Apple.
    2. También tendrás que configurar tu dominio del controlador de acciones de correo electrónico como un dominio asociado en las capacidades de la aplicación. De forma predeterminada, el controlador de acciones de correo electrónico se aloja en un dominio como se muestra en el siguiente ejemplo:
      APP_ID.firebaseapp.com
    3. Si planeas distribuir la aplicación para iOS 8 o versiones previas, deberás configurar el ID del paquete como un esquema personalizado para las URLs entrantes.
    4. Para obtener más información al respecto, consulta las instrucciones para recibir Dynamic Links en las plataformas de Apple.

Una vez que recibas el vínculo mencionado anteriormente, verifica que sea apto para la autenticación mediante vínculo de correo electrónico y completa el acceso.

Swift

if Auth.auth().isSignIn(withEmailLink: link) {
        Auth.auth().signIn(withEmail: email, link: self.link) { user, error in
          // ...
        }
}

Objective-C

if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
    [[FIRAuth auth] signInWithEmail:email
                               link:link
                         completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      // ...
    }];
}

Para obtener más información sobre cómo realizar el acceso mediante un vínculo de correo electrónico en una aplicación para Android, consulta la Guía para Android.

Para saber cómo realizar el acceso mediante un vínculo de correo electrónico en una aplicación web, consulta la Guía para aplicaciones web.

También puedes vincular este método de autenticación a un usuario existente. Por ejemplo, un usuario ya autenticado con otro proveedor, como un número de teléfono, puede agregar este método de acceso a la cuenta existente.

La segunda mitad de la operación sería diferente:

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.link(with: credential) { authData, error in
    if (error) {
      // And error occurred during linking.
      return
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthProvider credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkWithCredential:credential
              completion:^(FIRAuthDataResult *_Nullable result,
                           NSError *_Nullable error) {
    if (error) {
      // And error occurred during linking.
      return;
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }];

Esto también se puede usar para volver a autenticar a un usuario de un vínculo de correo electrónico antes de ejecutar una operación sensible.

Swift

  let credential = EmailAuthProvider.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticate(with: credential) { authData, error in
    if (error) {
      // And error occurred during re-authentication.
      return
    }
    // The user was successfully re-authenticated.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      reauthenticateWithCredential:credential
                        completion:^(FIRAuthDataResult *_Nullable result,
                                     NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

Sin embargo, como el flujo podría terminar en un dispositivo diferente, desde el cual el usuario original no accedió, este podría no completarse. En ese caso, se puede mostrar un error al usuario para forzarlo a abrir el vínculo en el mismo dispositivo. Se puede pasar algún estado en el vínculo para proporcionar información sobre el tipo de operación y el uid del usuario.

En caso de que admitas tanto contraseñas como accesos basados en vínculos de correo electrónico, usa fetchSignInMethodsForEmail a fin de diferenciar el método de acceso para un usuario con contraseña/vínculo. Esto es útil para flujos con identificador inicial, en los que primero se solicita al usuario que proporcione su correo electrónico y, luego, se le presenta el método de acceso:

Swift

 // After asking the user for their email.
 Auth.auth().fetchSignInMethods(forEmail: email) { signInMethods, error in
   // This returns the same array as fetchProviders(forEmail:completion:) but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (!signInMethods.contains(EmailPasswordAuthSignInMethod)) {
     // User can sign in with email/password.
   }
   if (!signInMethods.contains(EmailLinkAuthSignInMethod)) {
     // User can sign in with email/link.
   }
 }

Objective-C

 // After asking the user for their email.
 [FIRAuth auth] fetchSignInMethodsForEmail:email
                                completion:^(NSArray *_Nullable signInMethods,
                                             NSError *_Nullable error) {
   // This returns the same array as fetchProvidersForEmail but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (![signInMethods containsObject:FIREmailPasswordAuthSignInMethod]) {
     // User can sign in with email/password.
   }
   if (![signInMethods containsObject:FIREmailLinkAuthSignInMethod]) {
     // User can sign in with email/link.
   }
 }];

Como se describió anteriormente, correo electrónico/contraseña y correo electrónico/vínculo se consideran el mismo EmailAuthProvider (mismo PROVIDER_ID), pero con métodos de acceso diferentes.

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, puedes obtener la información básica de perfil del usuario a partir del objeto User. Consulta cómo administrar usuarios.

  • 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 la app con 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:.

Swift

let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}

Objective‑C

NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

Tal vez sea conveniente que agregues código de manejo de errores para todos los errores de autenticación. Consulta cómo solucionar errores.