Adicionar a autenticação multifator ao seu app do Flutter

Se você fez upgrade para o Firebase Authentication com o Identity Platform, poderá adicionar a autenticação multifator por SMS ao seu app do Flutter.

Esse recurso aumenta a segurança do app. Os invasores costumam comprometer as senhas e contas de redes sociais, mas é mais difícil interceptar uma mensagem de texto.

Antes de começar

  1. Ative pelo menos um provedor com suporte à autenticação multifator. Todos os provedores são compatíveis com a autenticação multifator (MFA), exceto as autenticações por smartphone e a anônima e o Apple Game Center.

  2. Confirme se o app está verificando os e-mails do usuário. A autenticação multifator (MFA) requer verificação de e-mail. Isso impede que agentes mal-intencionados se registrem em um serviço com um e-mail que não seja deles e bloqueiem o proprietário real adicionando um segundo fator.

  3. Android: caso você ainda não tenha definido o hash SHA-256 do seu app no Console do Firebase, faça isso. Consulte Como autenticar seu cliente para ver informações sobre como encontrar o hash SHA-256 do seu app.

  4. iOS: no Xcode, ative as notificações push para seu projeto e verifique se a sua chave de autenticação de APNs está configurada com o Firebase Cloud Messaging (FCM). Para ver uma explicação detalhada desta etapa, consulte a documentação Firebase iOS Phone Auth.

  5. Web: verifique se você adicionou o domínio dos seus aplicativos ao Console do Firebase em Domínios de redirecionamento OAuth.

Como ativar a autenticação multifator

  1. Abra a página Autenticação > Método de login do Console do Firebase.

  2. Na seção Avançado, ative Autenticação multifator por SMS.

    Insira também os números de telefone usados para testar o app. Embora opcional, é recomendável registrar números de telefone de teste para evitar a limitação durante o desenvolvimento.

  3. Se você ainda não autorizou o domínio do seu app, adicione-o à lista de permissões na página Autenticação > Configurações do Console do Firebase.

Como escolher um padrão de registro

É possível escolher se o app requer autenticação multifator e como e quando registrar os usuários. Alguns padrões comuns incluem:

  • Registrar o segundo fator do usuário como parte do processo. Use esse método se o app exigir autenticação multifator para todos os usuários.

  • Oferecer uma opção pulável para registrar um segundo fator durante o processo. Os apps que quiserem incentivar, mas não exigirem, a autenticação multifator podem ter essa abordagem.

  • Permitir a adição de um segundo fator na página de gerenciamento da conta ou no perfil do usuário, em vez da tela de inscrição. Isso minimiza o atrito durante o processo de registro, ao mesmo tempo que disponibiliza a autenticação multifator para usuários que se preocupam com a segurança.

  • Exigir a adição de um segundo fator de maneira incremental quando o usuário quiser acessar recursos com requisitos de segurança aprimorados.

Como registrar um segundo fator

Para registrar um novo fator secundário para um usuário:

  1. Reautentique o usuário.

  2. Peça ao usuário para inserir o número de telefone.

  3. Disponibilize uma sessão multifator para o usuário:

    final multiFactorSession = await user.multiFactor.getSession();
    
  4. Verifique o número de telefone com uma sessão multifator e suas callbacks:

    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. Após o envio do código SMS, peça ao usuário para fazer a verificação dele:

    final credential = PhoneAuthProvider.credential(
      verificationId: verificationId,
      smsCode: smsCode,
    );
    
  6. Conclua o registro:

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

O código abaixo mostra um exemplo completo de como registrar um segundo fator:

  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: (_) {},
  );

Parabéns! Você registrou um segundo fator de autenticação para um usuário.

Como fazer login dos usuários com um segundo fator

Para fazer login de um usuário com verificação por SMS de dois fatores:

  1. Faça login do usuário com o primeiro fator e, em seguida, capture a exceção FirebaseAuthMultiFactorException. Esse erro contém um resolvedor, que pode ser usado para conseguir o segundo fator registrado do usuário. Ele também contém uma sessão que prova a autenticação do usuário com o primeiro fator.

    Por exemplo, se o primeiro fator do usuário for um e-mail e uma senha:

    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. Se o usuário tiver vários fatores secundários registrados, pergunte a ele qual usar:

    final session = e.resolver.session;
    
    final hint = e.resolver.hints[selectedHint];
    
  3. Envie uma mensagem de verificação para o smartphone do usuário com a dica e a sessão multifator:

    await FirebaseAuth.instance.verifyPhoneNumber(
      multiFactorSession: session,
      multiFactorInfo: hint,
      verificationCompleted: (_) {},
      verificationFailed: (_) {},
      codeSent: (String verificationId, int? resendToken) async {
        // ...
      },
      codeAutoRetrievalTimeout: (_) {},
    );
    
  4. Chame resolver.resolveSignIn() para concluir a autenticação secundária:

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

O código abaixo mostra um exemplo completo de login para um usuário com vários fatores:

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) {
  ...
} 

Parabéns! Você fez login com sucesso usando uma autenticação multifator.

A seguir