Como adicionar a autenticação multifator com TOTP ao seu app Android

Se você fez upgrade para o Firebase Authentication with Identity Platform, poderá adicionar ao seu app a autenticação multifator (MFA) com uma senha única baseada em tempo (TOTP).

Firebase Authentication with Identity Platform permite que você use uma TOTP como um fator adicional para a MFA. Quando você ativa esse recurso, os usuários que tentam fazer login no app visualizam uma solicitação de um TOTP. Para gerá-la, é necessário usar um app autenticador capaz de gerar códigos TOTP válidos, como o Google Authenticator.

Antes de começar

  1. Ative pelo menos um provedor compatível com a autenticação multifator (MFA). Todos os provedores, exceto os seguintes MFA de suporte:

    • Autenticação por telefone
    • Autenticação anônima
    • Tokens de autenticação personalizados
    • Apple Game Center
  2. Verifique se o app verifica os endereços de e-mail dos usuários. 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 endereço de e-mail que não tenham e bloqueiem o proprietário real do endereço de e-mail adicionando um segundo fator.

  3. Instale o SDK do Firebase para Android caso ainda não tenha feito isso.

    A MFA com TOTP só é compatível com o SDK do Android versão v22.1.0 e mais recentes.

Ativar MFA TOTP

Para ativar a TOTP como segundo fator, use o Admin SDK ou chame o endpoint REST da configuração do projeto.

Para usar o Admin SDK, faça o seguinte:

  1. Instale o SDK Admin para Node.js do Firebase, caso ainda não tenha feito isso.

    O TOTP MFA só é compatível com o SDK Admin para Node.js do Firebase versão 11.6.0 e mais recentes.

  2. Execute o comando a seguir:

    import { getAuth } from 'firebase-admin/auth';
    
    getAuth().projectConfigManager().updateProjectConfig(
    {
          multiFactorConfig: {
              providerConfigs: [{
                  state: "ENABLED",
                  totpProviderConfig: {
                      adjacentIntervals: NUM_ADJ_INTERVALS
                  }
              }]
          }
    })
    

    Substitua:

    • NUM_ADJ_INTERVALS: o número de intervalos de janela de tempo adjacentes a partir dos quais aceitar TOTPs, de zero a dez. O padrão é cinco.

      Os TOTPs garantem que, quando duas partes (o responsável e o validador) gerarem OTPs na mesma janela de tempo (normalmente 30 segundos), elas gerem a mesma senha. No entanto, para acomodar o deslocamento do relógio entre as partes e o tempo de resposta humano, você pode configurar o serviço TOTP para aceitar também TOTPs de janelas adjacentes.

Para ativar a MTP TOTP usando a API REST, execute o seguinte:

curl -X PATCH "https://identitytoolkit.googleapis.com/admin/v2/projects/PROJECT_ID/config?updateMask=mfa" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: PROJECT_ID" \
    -d \
    '{
        "mfa": {
          "providerConfigs": [{
            "state": "ENABLED",
            "totpProviderConfig": {
              "adjacentIntervals": NUM_ADJ_INTERVALS
            }
          }]
       }
    }'

Substitua:

  • PROJECT_ID: o ID do projeto.
  • NUM_ADJ_INTERVALS: o número de intervalos de janela de tempo, de zero a dez. O padrão é cinco.

    Os TOTPs garantem que, quando duas partes (o responsável e o validador) gerarem OTPs na mesma janela de tempo (normalmente 30 segundos), elas gerem a mesma senha. No entanto, para acomodar o deslocamento do relógio entre as partes e o tempo de resposta humano, você pode configurar o serviço TOTP para aceitar também TOTPs de janelas adjacentes.

Escolha um padrão de inscrição

É 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 que pode ser ignorada para registrar um segundo fator durante o processo. Se você quiser incentivar, mas não exigir, a autenticação multifator no seu app, use 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.

Inscrever usuários na TOTP MFA

Depois de ativar a TOFA MFA como o segundo fator do app, implemente a lógica do cliente para registrar usuários na TOTP MFA:

  1. Reautentique o usuário.

  2. Gere um segredo TOTP para o usuário autenticado:

    // Generate a TOTP secret.
    Firebase.auth.currentUser.multiFactor.session
        .addOnSuccessListener { multiFactorSession ->
            TotpMultiFactorGenerator.generateSecret(multiFactorSession)
                .addOnSuccessListener { totpSecret ->
                    // Display the secret to the user and prompt them to
                    // enter it into their authenticator app. (See the next
                    // step.)
                }
        }
    
  3. Mostre a chave secreta ao usuário e solicite que ele a insira no app autenticador:

    // Display this key:
    val secret = totpSecret.sharedSecretKey
    

    Além de exibir a chave secreta, você pode tentar adicioná-la automaticamente ao app autenticador padrão do dispositivo. Para fazer isso, gere um URI de chave compatível com o Google Authenticator e o transmita para openInOtpApp():

    val qrCodeUri = totpSecret.generateQrCodeUrl(
        currentUser.email ?: "default account",
        "Your App Name")
    totpSecret.openInOtpApp(qrCodeUri)
    

    Depois que o usuário adiciona a chave secreta ao app autenticador, ele começa a gerar TOTPs.

  4. Peça ao usuário para digitar o TOTP exibido pelo app autenticador e usá-lo para finalizar o registro da MFA:

    // Ask the user for a verification code from the authenticator app.
    val verificationCode = // Code from user input.
    
    // Finalize the enrollment.
    val multiFactorAssertion = TotpMultiFactorGenerator
        .getAssertionForEnrollment(totpSecret, verificationCode)
    Firebase.auth.currentUser.multiFactor.enroll(multiFactorAssertion, "TOTP")
        .addOnSuccessListener {
            // Enrollment complete.
        }
    

Fazer login de usuários com um segundo fator

Para fazer login de usuários com a MFA TOTP, use o seguinte código:

  1. Chame um dos métodos signInWith como faria se não estivesse usando a MFA. Por exemplo, signInWithEmailAndPassword(). Se o método gerar um erro FirebaseAuthMultiFactorException, inicie o fluxo de MFA do app.

    Firebase.auth.signInWithEmailAndPassword(email, password)
        .addOnSuccessListener { result ->
            // If the user is not enrolled with a second factor and provided valid
            // credentials, sign-in succeeds.
    
            // (If your app requires MFA, this could be considered an error
            // condition, which you would resolve by forcing the user to enroll a
            // second factor.)
    
            // ...
        }
        .addOnFailureListener { exception ->
            when (exception) {
                is FirebaseAuthMultiFactorException -> {
                    // Initiate your second factor sign-in flow. (See next step.)
                    // ...
                }
            }
        }
    
  2. Primeiro, o fluxo de MFA do app solicitará que o usuário escolha o segundo fator que ele quer usar. Para conseguir uma lista de fatores secundários compatíveis, examine a propriedade hints de uma instância MultiFactorResolver:

    val enrolledFactors = exception.resolver.hints.map { it.displayName }
    
  3. Se o usuário optar por usar o TOTP, solicite que ele digite o TOTP exibido no app autenticador e o use para fazer login:

    when (exception.resolver.hints[selectedIndex].factorId) {
        TotpMultiFactorGenerator.FACTOR_ID -> {
            val otpFromAuthenticator = // OTP typed by the user.
            val assertion = TotpMultiFactorGenerator.getAssertionForSignIn(
                exception.resolver.hints[selectedIndex].uid,
                otpFromAuthenticator
            )
            exception.resolver.resolveSignIn(assertion)
                .addOnSuccessListener { result ->
                    // Successfully signed in!
                }
                .addOnFailureListener { resolveError ->
                    // Invalid or expired OTP.
                }
        }
        PhoneMultiFactorGenerator.FACTOR_ID -> {
            // Handle SMS second factor.
        }
    }
    

Cancelar inscrição na TOTP MFA

Esta seção descreve como lidar com um usuário que cancela a inscrição na TOFA MFA.

Se um usuário se inscrever em várias opções de MFA e se cancelar a inscrição na opção ativada mais recentemente, ele receberá uma auth/user-token-expired e será desconectado. O usuário precisará fazer login novamente e verificar as credenciais existentes, por exemplo, um endereço de e-mail e uma senha.

Para cancelar a inscrição do usuário, lidar com o erro e acionar a reautenticação, use o seguinte código:

Firebase.auth.currentUser.multiFactor.unenroll(mfaEnrollmentId)
    .addOnSuccessListener {
        // Second factor unenrolled.
    }
    .addOnFailureListener { exception ->
        when (exception) {
            is FirebaseAuthInvalidUserException -> {
                // Second factor unenrolled. If the user was signed out, re-authenticate
                // them.

                // For example, if they signed in with a password, prompt them to
                // provide it again, then call `reauthenticateWithCredential()` as shown
                // below.
                val credential = EmailAuthProvider.getCredential(email, password)
                currentUser.reauthenticate(credential)
                    .addOnSuccessListener { 
                        // Success!
                    }
                    .addOnFailureListener { 
                        // Bad email address and password combination.
                    }
            }
        }
    }

A seguir