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:

    authResult.user.multiFactor.getSessionWithCompletion() { (session, error) in
     
    // ...
    }
    [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 )

    // Send SMS verification code.
    PhoneAuthProvider.provider().verifyPhoneNumber(
      phoneNumber
    ,
      uiDelegate
    : nil,
      multiFactorSession
    : session) { (verificationId, error) in
       
    // verificationId will be needed for enrollment completion.
    }
    // 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 :

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

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    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).

    // Complete enrollment. This will update the underlying tokens
    // and trigger ID token change listener.
    user
    .multiFactor.enroll(with: assertion, displayName: displayName) { (error) in
     
    // ...
    }
    // 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:

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
     
// ...
   
}
 
}
})
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:

    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.
     
    }
    }
    [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 .

    // 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.
    }
    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:

    // 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.
    }
    // 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 :

    // Ask user for the verification code. Then:
    let credential
    = PhoneAuthProvider.provider().credential(
      withVerificationID
    : verificationId!,
      verificationCode
    : verificationCodeFromUser)
    // 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:

    let assertion = PhoneMultiFactorGenerator.assertion(with: credential)
    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:

    // 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.
    }
    // 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:

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.
       
}
     
}
   
}
 
}
}
[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