Adicione autenticação multifator TOTP ao seu aplicativo Android

Se você tiver atualizado para o Firebase Authentication com Identity Platform, poderá adicionar autenticação multifator (MFA) de senha única baseada em tempo (TOTP) ao seu aplicativo.

O Firebase Authentication com Identity Platform permite usar um TOTP como um fator adicional para MFA. Quando você habilita esse recurso, os usuários que tentam fazer login no seu aplicativo veem uma solicitação de TOTP. Para gerá-lo, eles devem utilizar um aplicativo autenticador capaz de gerar códigos TOTP válidos, como o Google Authenticator .

Antes de você começar

  1. Habilite pelo menos um provedor que ofereça suporte à MFA. Observe que todos os provedores , exceto os seguintes, oferecem suporte a MFA:

    • Autenticação de telefone
    • Autenticação anônima
    • Tokens de autenticação personalizados
    • Centro de jogos da Apple
  2. Certifique-se de que seu aplicativo verifique os endereços de e-mail dos usuários. A MFA requer verificação de e-mail. Isso evita que agentes mal-intencionados se registrem em um serviço com um endereço de e-mail que não lhes pertence e, em seguida, bloqueiem o proprietário real do endereço de e-mail adicionando um segundo fator.

  3. Caso ainda não tenha feito isso, instale o Firebase Android SDK .

    TOTP MFA é compatível apenas com Android SDK versão v22.1.0 e superior.

Habilitar TOTP MFA

Para habilitar o TOTP como um segundo fator, use o SDK Admin ou chame o endpoint REST de configuração do projeto.

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

  1. Caso ainda não tenha feito isso, instale o SDK Admin Node.js do Firebase .

    TOTP MFA é compatível apenas com o SDK Admin Node.js do Firebase versões 11.6.0 e superiores.

  2. Execute o seguinte:

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

    Substitua o seguinte:

    • 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 funcionam garantindo que quando duas partes (o provador e o validador) geram OTPs dentro da mesma janela de tempo (normalmente 30 segundos de duração), elas geram a mesma senha. No entanto, para acomodar o desvio do relógio entre as partes e o tempo de resposta humana, você pode configurar o serviço TOTP para também aceitar TOTPs de janelas adjacentes.

Para ativar o TOTP MFA 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 o seguinte:

  • 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 funcionam garantindo que quando duas partes (o provador e o validador) geram OTPs dentro da mesma janela de tempo (normalmente 30 segundos de duração), elas geram a mesma senha. No entanto, para acomodar o desvio do relógio entre as partes e o tempo de resposta humana, você pode configurar o serviço TOTP para também aceitar TOTPs de janelas adjacentes.

Escolha um padrão de inscrição

Você pode escolher se seu aplicativo requer autenticação multifator e como e quando inscrever seus usuários. Alguns padrões comuns incluem o seguinte:

  • Inscreva o segundo fator do usuário como parte do registro. Use este método se seu aplicativo exigir autenticação multifator para todos os usuários.

  • Ofereça uma opção ignorável para inscrever um segundo fator durante o registro. Se quiser incentivar, mas não exigir, autenticação multifator em seu aplicativo, você pode usar esta abordagem.

  • Forneça a capacidade de adicionar um segundo fator da conta do usuário ou da página de gerenciamento de perfil, 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 sensíveis à segurança.

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

Inscrever usuários no TOTP MFA

Depois de ativar o TOTP MFA como um segundo fator para seu aplicativo, implemente a lógica do lado do cliente para inscrever usuários no TOTP MFA:

  1. Autentique novamente 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. Exiba o segredo ao usuário e solicite que ele o insira no aplicativo autenticador:

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

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

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

    Depois que o usuário adicionar seu segredo ao aplicativo autenticador, ele começará a gerar TOTPs.

  4. Solicite ao usuário que digite o TOTP exibido pelo aplicativo autenticador e use-o para finalizar o registro do 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.
        }
    

Faça login de usuários com um segundo fator

Para conectar usuários com TOTP MFA, use o seguinte código:

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

    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. O fluxo de MFA do seu aplicativo deve primeiro solicitar que o usuário escolha o segundo fator que deseja usar. Você pode obter uma lista de segundos fatores suportados examinando 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 aplicativo autenticador e use-o 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 no TOTP MFA

Esta seção descreve como lidar com o cancelamento da inscrição de um usuário no TOTP MFA.

Se um usuário se inscreveu em várias opções de MFA e cancelou a inscrição na opção habilitada mais recentemente, ele receberá um auth/user-token-expired e será desconectado. O usuário deve 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, tratar 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.
                    }
            }
        }
    }

Qual é o próximo