Adicione autenticação multifator ao seu aplicativo iOS

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

A autenticação multifator aumenta a segurança do seu aplicativo. Embora os invasores frequentemente comprometam senhas e contas sociais, interceptar uma mensagem de texto é mais difícil.

Antes de você começar

  1. Habilite pelo menos um provedor que ofereça suporte à autenticação multifator. Todos os provedores oferecem suporte a MFA, exceto autenticação por telefone, autenticação anônima e Apple Game Center.

  2. Certifique-se de que seu aplicativo esteja verificando os e-mails dos usuários. A MFA requer verificação de e-mail. Isso evita que atores mal-intencionados se registrem em um serviço com um e-mail que não sejam de sua propriedade e, em seguida, bloqueiem o proprietário real adicionando um segundo fator.

Habilitando 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 , habilite a autenticação multifator de SMS .

    Você também deve inserir os números de telefone com os quais testará seu aplicativo. Embora opcional, é altamente recomendável registrar números de telefone de teste para evitar limitações durante o desenvolvimento.

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

Verificando seu aplicativo

O Firebase precisa verificar se as solicitações de SMS vêm do seu aplicativo. Você pode fazer isso de duas maneiras:

  • Notificações silenciosas de APNs : quando você faz login de um usuário pela primeira vez, o Firebase pode enviar uma notificação push silenciosa para o dispositivo do usuário. A autenticação poderá prosseguir se o aplicativo receber a notificação. Observe que a partir do iOS 8.0, você não precisa solicitar ao usuário que permita notificações push para usar esse método.

  • Verificação reCAPTCHA : se você não conseguir enviar uma notificação silenciosa (por exemplo, porque o usuário desativou a atualização em segundo plano ou você está testando seu aplicativo no simulador iOS), poderá usar o reCAPTCHA. Em muitos casos, o reCAPTCHA se resolverá automaticamente, sem interação do usuário.

Usando notificações silenciosas

Para ativar notificações de APNs para uso com o Firebase:

  1. No Xcode, habilite notificações push para seu projeto.

  2. Faça upload da sua chave de autenticação de APNs usando o Firebase Console (suas alterações serão transferidas automaticamente para o Google Cloud Firebase). Se você ainda não tem sua chave de autenticação de APNs, consulte Configurando APNs com FCM para saber como obtê-la.

    1. Abra o Console do Firebase .

    2. Navegue até Configurações do projeto .

    3. Selecione a guia Mensagens na nuvem .

    4. Em Chave de autenticação de APNs , na seção de configuração do aplicativo iOS , clique em Carregar .

    5. Selecione sua chave.

    6. Adicione o ID da chave. Você pode encontrar o ID da chave em Certificados, identificadores e perfis no Apple Developer Member Center .

    7. Clique em Carregar .

Se você já tiver um certificado de APNs, poderá fazer upload do certificado.

Usando verificação reCAPTCHA

Para permitir que o SDK do cliente use o reCAPTCHA:

  1. Abra a configuração do seu projeto no Xcode.

  2. Clique duas vezes no nome do projeto na visualização em árvore esquerda.

  3. Selecione seu aplicativo na seção Destinos .

  4. Selecione a guia Informações .

  5. Expanda a seção Tipos de URL .

  6. Clique no botão + .

  7. Insira seu ID de cliente invertido no campo Esquemas de URL . Você pode encontrar esse valor listado no arquivo de configuração GoogleService-Info.plist como REVERSED_CLIENT_ID .

Quando concluída, sua configuração deverá ser semelhante à seguinte:

Esquemas personalizados

Opcionalmente, você pode personalizar a forma como seu aplicativo apresenta o SFSafariViewController ou UIWebView ao exibir o reCAPTCHA. Para fazer isso, crie uma classe personalizada que esteja em conformidade com o protocolo FIRAuthUIDelegate e passe-a para verifyPhoneNumber:UIDelegate:completion: .

Escolhendo 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:

  • 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. Observe que uma conta deve ter um endereço de e-mail verificado para inscrever um segundo fator, portanto, seu fluxo de registro deverá acomodar isso.

  • Ofereça uma opção ignorável para inscrever um segundo fator durante o registro. Os aplicativos que desejam incentivar, mas não exigem, a autenticação multifator podem preferir essa abordagem.

  • Forneça a capacidade de adicionar um segundo fator na conta do usuário ou na 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.

Inscrevendo um segundo fator

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

  1. Autentique novamente o usuário.

  2. Peça ao usuário que insira seu número de telefone.

  3. Obtenha uma sessão multifatorial para o usuário:

    Rápido

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
      // ...
    }
    

    Objetivo-C

    [authResult.user.multiFactor
      getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                NSError * _Nullable error) {
        // ...
    }];
    
  4. Envie uma mensagem de verificação para o telefone do usuário. Certifique-se de que o número de telefone esteja formatado com um + inicial e nenhuma outra pontuação ou espaço em branco (por exemplo: +15105551234 )

    Rápido

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber,
      uiDelegate: nil,
      multiFactorSession: session) { (verificationId, error) in
        // verificationId will be needed for enrollment completion.
    }
    

    Objetivo-C

    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber
                                          UIDelegate:nil
                                  multiFactorSession:session
                                          completion:^(NSString * _Nullable verificationID,
                                                        NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.
    }];
    

    Embora não seja obrigatório, é uma prática recomendada informar antecipadamente aos usuários que eles receberão uma mensagem SMS e que serão aplicadas taxas padrão.

    O método verifyPhoneNumber() inicia o processo de verificação do aplicativo em segundo plano usando notificação push silenciosa. Se a notificação push silenciosa não estiver disponível, um desafio reCAPTCHA será emitido.

  5. Assim que o código SMS for enviado, peça ao usuário para verificar o código. Em seguida, use a resposta deles para criar um PhoneAuthCredential :

    Rápido

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: verificationCode)
    

    Objetivo-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential = [FIRPhoneAuthProvider.provider
                                           credentialWithVerificationID:verificationID
                                           verificationCode:kPhoneSecondFactorVerificationCode];
    
  6. Inicialize um objeto de asserção:

    Rápido

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objetivo-C

    FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  7. Conclua a inscrição. Opcionalmente, você pode especificar um nome de exibição para o segundo fator. Isso é útil para usuários com vários segundos fatores, pois o número de telefone é mascarado durante o fluxo de autenticação (por exemplo, +1******1234).

    Rápido

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
    

    Objetivo-C

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    [authResult.user.multiFactor enrollWithAssertion:assertion
                                         displayName:nil
                                          completion:^(NSError * _Nullable error) {
        // ...
    }];
    

O código abaixo mostra um exemplo completo de inscrição de um segundo fator:

Rápido

let user = Auth.auth().currentUser
user?.multiFactor.getSessionWithCompletion({ (session, error) in
  // Send SMS verification code.
  PhoneAuthProvider.provider().verifyPhoneNumber(
    phoneNumber,
    uiDelegate: nil,
    multiFactorSession: session
  ) { (verificationId, error) in
    // verificationId will be needed for enrollment completion.
    // Ask user for the verification code.
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: phoneSecondFactorVerificationCode)
    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user?.multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
      // ...
    }
  }
})

Objetivo-C

FIRUser *user = FIRAuth.auth.currentUser;
[user.multiFactor getSessionWithCompletion:^(FIRMultiFactorSession * _Nullable session,
                                              NSError * _Nullable error) {
    // Send SMS verification code.
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumber:phoneNumber
      UIDelegate:nil
      multiFactorSession:session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        // verificationId will be needed for enrollment completion.

        // Ask user for the verification code.
        // ...

        // Then:
        FIRPhoneAuthCredential *credential =
            [FIRPhoneAuthProvider.provider credentialWithVerificationID:verificationID
                                                        verificationCode:kPhoneSecondFactorVerificationCode];
        FIRMultiFactorAssertion *assertion =
            [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

        // Complete enrollment. This will update the underlying tokens
        // and trigger ID token change listener.
        [user.multiFactor enrollWithAssertion:assertion
                                  displayName:displayName
                                    completion:^(NSError * _Nullable error) {
            // ...
        }];
    }];
}];

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

Conectando usuários com um segundo fator

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

  1. Faça login do usuário com seu primeiro fator e, em seguida, detecte um erro indicando que a autenticação multifator é necessária. Este erro contém um resolvedor, dicas sobre os segundos fatores registrados e uma sessão subjacente que prova que o usuário foi autenticado com sucesso com o primeiro fator.

    Por exemplo, se o primeiro fator do usuário for email e senha:

    Rápido

    Auth.auth().signIn(
      withEmail: email,
      password: password
    ) { (result, error) in
      let authError = error as NSError
      if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
        // The user is a multi-factor user. Second factor challenge is required.
        let resolver =
          authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
        // ...
      } else {
        // Handle other errors such as wrong password.
      }
    }
    

    Objetivo-C

    [FIRAuth.auth signInWithEmail:email
                         password:password
                       completion:^(FIRAuthDataResult * _Nullable authResult,
                                    NSError * _Nullable error) {
        if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
            // User is not enrolled with a second factor and is successfully signed in.
            // ...
        } else {
            // The user is a multi-factor user. Second factor challenge is required.
        }
    }];
    

    Se o primeiro fator do usuário for um provedor federado, como OAuth, detecte o erro após chamar getCredentialWith() .

  2. Se o usuário tiver vários fatores secundários inscritos, pergunte qual deles usar. Você pode obter o número de telefone mascarado com resolver.hints[selectedIndex].phoneNumber e o nome de exibição com resolver.hints[selectedIndex].displayName .

    Rápido

    // Ask user which second factor to use. Then:
    if resolver.hints[selectedIndex].factorID == PhoneMultiFactorID {
      // User selected a phone second factor.
      // ...
    } else if resolver.hints[selectedIndex].factorID == TotpMultiFactorID {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    

    Objetivo-C

    FIRMultiFactorResolver *resolver =
        (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
    
    // Ask user which second factor to use. Then:
    FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];
    if (hint.factorID == FIRPhoneMultiFactorID) {
      // User selected a phone second factor.
      // ...
    } else if (hint.factorID == FIRTOTPMultiFactorID) {
      // User selected a TOTP second factor.
      // ...
    } else {
      // Unsupported second factor.
    }
    
  3. Envie uma mensagem de verificação para o telefone do usuário:

    Rápido

    // Send SMS verification code.
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      // verificationId will be needed for sign-in completion.
    }
    

    Objetivo-C

    // Send SMS verification code
    [FIRPhoneAuthProvider.provider
      verifyPhoneNumberWithMultiFactorInfo:hint
      UIDelegate:nil
      multiFactorSession:resolver.session
      completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
        if (error != nil) {
            // Failed to verify phone number.
        }
    }];
    
  4. Assim que o código SMS for enviado, peça ao usuário para verificar o código e usá-lo para criar um PhoneAuthCredential :

    Rápido

    // Ask user for the verification code. Then:
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId!,
      verificationCode: verificationCodeFromUser)
    

    Objetivo-C

    // Ask user for the SMS verification code. Then:
    FIRPhoneAuthCredential *credential =
        [FIRPhoneAuthProvider.provider
          credentialWithVerificationID:verificationID
                      verificationCode:verificationCodeFromUser];
    
  5. Inicialize um objeto de asserção com a credencial:

    Rápido

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    

    Objetivo-C

    FIRMultiFactorAssertion *assertion =
        [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
    
  6. Resolva o login. Você pode então acessar o resultado de login original, que inclui os dados padrão específicos do provedor e as credenciais de autenticação:

    Rápido

    // Complete sign-in. This will also trigger the Auth state listeners.
    resolver.resolveSignIn(with: assertion) { (authResult, error) in
      // authResult will also contain the user, additionalUserInfo, optional
      // credential (null for email/password) associated with the first factor sign-in.
    
      // For example, if the user signed in with Google as a first factor,
      // authResult.additionalUserInfo will contain data related to Google provider that
      // the user signed in with.
    
      // user.credential contains the Google OAuth credential.
      // user.credential.accessToken contains the Google OAuth access token.
      // user.credential.idToken contains the Google OAuth ID token.
    }
    

    Objetivo-C

    // Complete sign-in.
    [resolver resolveSignInWithAssertion:assertion
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                            NSError * _Nullable error) {
        if (error != nil) {
            // User successfully signed in with the second factor phone number.
        }
    }];
    

O código abaixo mostra um exemplo completo de login de um usuário multifator:

Rápido

Auth.auth().signIn(
  withEmail: email,
  password: password
) { (result, error) in
  let authError = error as NSError?
  if authError?.code == AuthErrorCode.secondFactorRequired.rawValue {
    let resolver =
      authError!.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver

    // Ask user which second factor to use.
    // ...

    // Then:
    let hint = resolver.hints[selectedIndex] as! PhoneMultiFactorInfo

    // Send SMS verification code
    PhoneAuthProvider.provider().verifyPhoneNumber(
      with: hint,
      uiDelegate: nil,
      multiFactorSession: resolver.session
    ) { (verificationId, error) in
      if error != nil {
        // Failed to verify phone number.
      }
      // Ask user for the SMS verification code.
      // ...

      // Then:
      let credential = PhoneAuthProvider.provider().credential(
        withVerificationID: verificationId!,
        verificationCode: verificationCodeFromUser)
      let assertion = PhoneMultiFactorGenerator.assertion(with: credential)

      // Complete sign-in.
      resolver.resolveSignIn(with: assertion) { (authResult, error) in
        if error != nil {
          // User successfully signed in with the second factor phone number.
        }
      }
    }
  }
}

Objetivo-C

[FIRAuth.auth signInWithEmail:email
                     password:password
                   completion:^(FIRAuthDataResult * _Nullable authResult,
                               NSError * _Nullable error) {
    if (error == nil || error.code != FIRAuthErrorCodeSecondFactorRequired) {
        // User is not enrolled with a second factor and is successfully signed in.
        // ...
    } else {
        FIRMultiFactorResolver *resolver =
            (FIRMultiFactorResolver *) error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];

        // Ask user which second factor to use.
        // ...

        // Then:
        FIRPhoneMultiFactorInfo *hint = (FIRPhoneMultiFactorInfo *) resolver.hints[selectedIndex];

        // Send SMS verification code
        [FIRPhoneAuthProvider.provider
          verifyPhoneNumberWithMultiFactorInfo:hint
                                    UIDelegate:nil
                            multiFactorSession:resolver.session
                                    completion:^(NSString * _Nullable verificationID,
                                                NSError * _Nullable error) {
            if (error != nil) {
                // Failed to verify phone number.
            }

            // Ask user for the SMS verification code.
            // ...

            // Then:
            FIRPhoneAuthCredential *credential =
                [FIRPhoneAuthProvider.provider
                  credentialWithVerificationID:verificationID
                              verificationCode:kPhoneSecondFactorVerificationCode];
            FIRMultiFactorAssertion *assertion =
                [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];

            // Complete sign-in.
            [resolver resolveSignInWithAssertion:assertion
                                      completion:^(FIRAuthDataResult * _Nullable authResult,
                                                    NSError * _Nullable error) {
                if (error != nil) {
                    // User successfully signed in with the second factor phone number.
                }
            }];
        }];
    }
}];

Parabéns! Você fez login com êxito em um usuário usando autenticação multifator.

Qual é o próximo