Agrega la autenticación de varios factores a una app para Flutter

Si actualizaste a Firebase Authentication con Identity Platform, puedes agregar la autenticación de varios factores mediante SMS a tu app para Flutter.

La autenticación de varios factores (MFA) aumenta la seguridad de tu app. Si bien los atacantes a menudo hackean las contraseñas y las cuentas de redes sociales, interceptar un mensaje de texto es más difícil.

Antes de comenzar

  1. Habilita al menos un proveedor que admita la autenticación de varios factores. Todos los proveedores admiten la MFA, excepto la autenticación por teléfono, la autenticación anónima y Game Center de Apple.

  2. Asegúrate de que en tu app se verifiquen los correos electrónicos de los usuarios. La MFA requiere la verificación por correo electrónico. Esto evita que los actores maliciosos se registren en un servicio con un correo electrónico que no les pertenece y, luego, agreguen un segundo factor para bloquear al propietario real.

  3. Android: Si aún no has configurado el hash SHA-256 de tu app, hazlo en Firebase console. Consulta Authenticating Your Client para obtener información sobre cómo encontrar el hash SHA-256 de tu app.

  4. iOS: En Xcode, habilita las notificaciones push para el proyecto y asegúrate de que la clave de autenticación de APNS esté configurada con Firebase Cloud Messaging (FCM). Si deseas obtener una explicación detallada de este paso, consulta la documentación de Autenticación telefónica de iOS para Firebase.

  5. Web: Asegúrate de haber agregado el dominio de tus aplicaciones en Firebase console, en Dominios de redireccionamiento de OAuth.

Habilita la autenticación de varios factores

  1. Abre la página Autenticación > Método de acceso de Firebase console.

  2. En la sección Opciones avanzadas, habilita la Autenticación de varios factores mediante SMS.

    También debes ingresar los números de teléfono con los que probarás la app. Si bien es opcional, se recomienda registrar los números de teléfono de prueba para evitar los límites durante el desarrollo.

  3. Si aún no autorizas el dominio de tu app, agrégalo a la lista de entidades permitidas en la página Autenticación > Configuración de Firebase console.

Elige un patrón de inscripción

Puedes elegir si tu app requerirá una autenticación de varios factores, además de cómo y cuándo inscribir a tus usuarios. Entre los patrones comunes, se incluyen los siguientes:

  • Inscribir el segundo factor del usuario como parte del registro. Usa este método si tu app requiere la autenticación de varios factores para todos los usuarios.

  • Ofrecer una opción que se puede omitir para inscribir un segundo factor durante el registro. Es posible que las apps que quieran fomentar el proceso de autenticación de varios factores, pero que no lo requieran, prefieran este enfoque.

  • Proporcionar la capacidad de agregar un segundo factor desde la página de administración de la cuenta o el perfil del usuario, en lugar de la pantalla de registro. Esto minimiza la fricción durante el proceso de registro y, a la vez, permite que la autenticación de varios factores esté disponible para los usuarios sensibles a la seguridad.

  • Requiere agregar un segundo factor de manera incremental cuando el usuario quiera acceder a las funciones con requisitos de seguridad mayores.

Inscribe un segundo factor

Si quieres inscribir un nuevo factor secundario para un usuario, haz lo siguiente:

  1. Vuelve a autenticar al usuario.

  2. Pídele al usuario que ingrese su número de teléfono.

  3. Obtén una sesión de varios factores para el usuario:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. Verifica el número de teléfono con una sesión de varios factores y tus devoluciones de llamada:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: multiFactorSession,
      phoneNumber: phoneNumber,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // The SMS verification code has been sent to the provided phone number.
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    ); 
    
  5. Después de que se envíe el código SMS, pídele al usuario que lo verifique:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. Completa la inscripción.

    await user.multiFactor.enroll(
      PhoneMultiFactorGenerator.getAssertion(
        credential,
      ),
    );
    

En el siguiente código, se muestra un ejemplo completo de la inscripción de un segundo factor:

  final session = await user.multiFactor.getSession();
  final auth = FirebaseAuth.instance;
  await auth.verifyPhoneNumber(
    multiFactorSession: session,
    phoneNumber: phoneController.text,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await user.multiFactor.enroll(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );

¡Felicitaciones! Registraste correctamente un segundo factor de autenticación para un usuario.

Permite que los usuarios accedan con un segundo factor

Para que un usuario acceda con la verificación mediante SMS de dos factores, haz lo siguiente:

  1. Haz que el usuario acceda con el primer factor y, luego, detecta la excepción FirebaseAuthMultiFactorException. Este error contiene un agente de resolución, que puedes usar para obtener los segundos factores inscritos del usuario. También contiene una sesión subyacente que demuestra que el usuario se autenticó correctamente con su primer factor.

    Por ejemplo, si el primer factor del usuario era un correo electrónico y una contraseña, haz lo siguiente:

    try {
      await _auth.signInWithEmailAndPassword(
          email: emailController.text,
          password: passwordController.text,
      );
      // User is not enrolled with a second factor and is successfully
      // signed in.
      // ...
    } on FirebaseAuthMultiFactorException catch (e) {
      // The user is a multi-factor user. Second factor challenge is required
      final resolver = e.resolver
      // ...
    }
    
  2. Si el usuario tiene múltiples factores secundarios inscritos, pregúntale cuál usará:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. Envía un mensaje de verificación al teléfono del usuario con la sugerencia y la sesión de varios factores:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. Llama a resolver.resolveSignIn() para completar la autenticación secundaria.

    final smsCode = await getSmsCodeFromUser(context);
    if (smsCode != null) {
      // Create a PhoneAuthCredential with the code
      final credential = PhoneAuthProvider.credential(
        verificationId: verificationId,
        smsCode: smsCode,
      );
    
      try {
        await e.resolver.resolveSignIn(
          PhoneMultiFactorGenerator.getAssertion(credential)
        );
      } on FirebaseAuthException catch (e) {
        print(e.message);
      }
    }
    

En el siguiente código, se muestra un ejemplo completo de acceso de un usuario de varios factores:

try {
  await _auth.signInWithEmailAndPassword(
    email: emailController.text,
    password: passwordController.text,
  );
} on FirebaseAuthMultiFactorException catch (e) {
  setState(() {
    error = '${e.message}';
  });
  final firstHint = e.resolver.hints.first;
  if (firstHint is! PhoneMultiFactorInfo) {
    return;
  }
  await FirebaseAuth.instance.verifyPhoneNumber(
    multiFactorSession: e.resolver.session,
    multiFactorInfo: firstHint,
    verificationCompleted: (_) {},
    verificationFailed: (_) {},
    codeSent: (String verificationId, int? resendToken) async {
      // See `firebase_auth` example app for a method of retrieving user's sms code: 
      // https://github.com/firebase/flutterfire/blob/master/packages/firebase_auth/firebase_auth/example/lib/auth.dart#L591
      final smsCode = await getSmsCodeFromUser(context);

      if (smsCode != null) {
        // Create a PhoneAuthCredential with the code
        final credential = PhoneAuthProvider.credential(
          verificationId: verificationId,
          smsCode: smsCode,
        );

        try {
          await e.resolver.resolveSignIn(
            PhoneMultiFactorGenerator.getAssertion(
              credential,
            ),
          );
        } on FirebaseAuthException catch (e) {
          print(e.message);
        }
      }
    },
    codeAutoRetrievalTimeout: (_) {},
  );
} catch (e) {
  ...
} 

¡Felicitaciones! Permitiste acceder a un usuario correctamente mediante la autenticación de varios factores.

Próximos pasos