Use o Firebase Authentication para fazer o login de um usuário. Basta enviar uma mensagem SMS para o telefone dele. O usuário faz login com um código de uso único contido na mensagem SMS.
A maneira mais fácil de adicionar o login com número de telefone ao app é usar a FirebaseUI. Ela inclui um widget drop-in que implementa fluxos para logins com número de telefone, bem como logins por senha ou federados. Este documento descreve como implementar um fluxo de login com número de telefone pelo SDK do Firebase.
Antes de começar
Use o Swift Package Manager para instalar e gerenciar as dependências do Firebase.
- No Xcode, com seu projeto do app aberto, navegue até File > Add Packages.
- Quando solicitado, adicione o repositório do SDK do Firebase para as plataformas Apple:
- Escolha a biblioteca do Firebase Authentication.
- Quando terminar, o Xcode começará a resolver e fazer o download das dependências em segundo plano automaticamente.
https://github.com/firebase/firebase-ios-sdk
- Caso você ainda não tenha vinculado o app ao projeto do Firebase, faça isso no Console do Firebase.
Preocupações com segurança
A autenticação usando apenas um número de telefone é conveniente, porém, menos segura do que os outros métodos disponíveis, já que um número de telefone pode ser facilmente transferido entre usuários. Além disso, em dispositivos com vários perfis de usuário, qualquer um que receba mensagens SMS pode fazer login em uma conta usando o número de telefone do dispositivo.
Caso use o login com número de telefone no seu app, você precisa oferecê-lo junto com métodos de login mais seguros e informar aos usuários as implicações de segurança do uso desse tipo de login.
Ativar o login com número de telefone para o projeto do Firebase
Para fazer login de usuários por SMS, ative primeiro o método de login com número de telefone no projeto do Firebase:
- No Console do Firebase, abra a seção Autenticação.
- Na página Método de login, ative o método de login com número de telefone.
A cota de solicitações de login com número de telefone do Firebase é alta o suficiente para evitar que a maioria dos apps seja afetada. No entanto, se for necessário fazer login de um grande volume de usuários com autenticação por telefone, atualize o plano de preços. Consulte a página de preços.
Ativar a verificação do aplicativo
Para usar a autenticação por número de telefone, o Firebase precisa verificar se as solicitações de login do número de telefone estão vindo do seu aplicativo. Existem duas formas de fazer isso:
- Notificações de APNs silenciosas: quando você faz login de um usuário com o número de telefone pela primeira vez em um dispositivo, o Firebase Authentication envia um token para o dispositivo usando uma notificação push silenciosa. Se o aplicativo recebe com êxito a notificação do Firebase, o login com o número de telefone pode prosseguir.
Para o iOS 8.0 e versões mais recentes, as notificações silenciosas não exigem o consentimento explícito do usuário e, portanto, não são afetadas por um usuário que escolha não receber notificações de APNs no app. Assim, o app não precisa solicitar a permissão do usuário para receber notificações push ao implementar a autenticação com o número de telefone do Firebase.
- Verificação reCAPTCHA: nos casos em que o envio ou recebimento de uma notificação push silenciosa não é possível, como quando o usuário desativou a atualização em segundo plano para seu app ou ao testar o aplicativo em um simulador de iOS, o Firebase Authentication usa a verificação reCAPTCHA para concluir o fluxo de login com número de telefone. O desafio do reCAPTCHA pode ser concluído sem que o usuário tenha que resolver nada.
Quando as notificações push silenciosas são configuradas corretamente, apenas uma porcentagem muito pequena de usuários terá experiências com o fluxo reCAPTCHA. No entanto, você precisa garantir que o login com número de telefone funcione de maneira correta, independentemente de as notificações push silenciosas estarem ou não disponíveis.
Receber notificações silenciosas
Para ativar notificações de APNs para uso com o Firebase Authentication:
- No Xcode, ative notificações push para seu projeto.
-
Faça upload da chave de autenticação de APNs para o Firebase. Se você ainda não tiver uma chave de autenticação de APNs, crie uma no Apple Developer Member Center.
-
No seu projeto no Console do Firebase, selecione o ícone de engrenagem, Configurações do projeto e a guia Cloud Messaging.
-
Acesse a Configuração do app iOS. Em Chave de autenticação de APNs, clique no botão Fazer upload.
-
Navegue até o local onde você salvou a chave, selecione-a e clique em Abrir. Adicione o ID da chave disponível na Apple Developer Member Center e clique em Fazer upload.
Faça upload do certificado se já tiver um certificado APN.
-
Configurar a verificação reCAPTCHA
Para ativar o SDK do Firebase para usar a verificação reCAPTCHA:
- Adicione esquemas de URL personalizado ao seu projeto do XCode:
- Abra a configuração do seu projeto clicando duas vezes no nome dele na visualização em árvore à esquerda. Selecione seu app na seção DESTINOS. Em seguida, selecione a guia Informações e expanda a seção Tipos de URL.
- Clique no botão + e adicione o ID do app codificado como um esquema de URL. Encontre o ID codificado do app na página Configurações gerais do Console do Firebase na seção do seu app iOS. Deixe os outros campos em branco.
Quando concluída, a configuração será semelhante à mostrada a seguir, mas com os valores específicos do seu app:
- Opcional: se você quiser personalizar a forma como o app apresenta o
SFSafariViewController
ao exibir o reCAPTCHA para o usuário, crie uma classe personalizada que esteja em conformidade com o protocoloAuthUIDelegate
e transmita-a paraverifyPhoneNumber(_:uiDelegate:completion:)
.
Enviar um código de verificação ao telefone do usuário
Para iniciar o login com número de telefone, apresente ao usuário uma interface que solicite
o número dele e, em seguida, chame
verifyPhoneNumber(_:uiDelegate:completion:)
para pedir que o Firebase
envie um código de autenticação por SMS ao smartphone do usuário:
-
Solicite o número de telefone do usuário.
Os requisitos legais variam, mas como prática recomendada e para definir as expectativas dos usuários, informe a eles que, se fizerem login com telefone, poderão receber uma mensagem SMS para verificação. Além disso, poderão ser cobrados por esse serviço.
- Chame
verifyPhoneNumber(_:uiDelegate:completion:)
e transmita a ele o número de telefone do usuário.Swift
PhoneAuthProvider.provider() .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in if let error = error { self.showMessagePrompt(error.localizedDescription) return } // Sign in using the verificationID and the code sent to the user // ... }
Objective-C
[[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput UIDelegate:nil completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; return; } // Sign in using the verificationID and the code sent to the user // ... }];
O método
verifyPhoneNumber
é reentrante: se você chamá-lo várias vezes, como no métodoonAppear
de uma visualização, o métodoverifyPhoneNumber
não vai enviar um segundo SMS, a menos que a solicitação original tenha expirado.Quando você chama
verifyPhoneNumber(_:uiDelegate:completion:)
, o Firebase envia uma notificação push silenciosa para o app ou emite um desafio reCAPTCHA para o usuário. Depois que seu app receber a notificação ou o usuário completar o desafio reCAPTCHA, o Firebase envia uma mensagem SMS com um código de autenticação ao número de telefone especificado e transmite uma identificação da verificação para a função de conclusão. Você vai precisar do código e do ID de verificação para fazer o login do usuário.A mensagem SMS enviada pelo Firebase também pode ser localizada ao especificar a linguagem de autenticação por meio da propriedade
languageCode
na sua instância Auth.Swift
// Change language code to french. Auth.auth().languageCode = "fr";
Objective-C
// Change language code to french. [FIRAuth auth].languageCode = @"fr";
-
Salve a identificação da verificação e restaure-a depois que o app for carregado. Ao fazer isso, você tem a garantia de ter um ID de verificação válido caso o app seja encerrado antes que o usuário conclua o fluxo de login (por exemplo, ao alternar para o app de SMS).
Prossiga com a identificação de verificação da maneira que quiser. Uma maneira simples é salvar a identificação da verificação com o objeto
NSUserDefaults
:Swift
UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
Objective-C
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:verificationID forKey:@"authVerificationID"];
Em seguida, você poderá restaurar o valor salvo:
Swift
let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
Objective-C
NSString *verificationID = [defaults stringForKey:@"authVerificationID"];
Se a chamada para verifyPhoneNumber(_:uiDelegate:completion:)
tiver sucesso, será possível solicitar que o usuário digite o código de verificação quando
recebê-lo na mensagem SMS.
Fazer login do usuário com o código de verificação
Depois que o usuário informar o código de verificação da mensagem SMS
ao app, faça o login desse usuário. Para isso, crie um objeto FIRPhoneAuthCredential
por meio do código e do ID de verificação e o transmita para
signInWithCredential:completion:
.
- Solicite o código de verificação ao usuário.
- Crie um objeto
FIRPhoneAuthCredential
a partir do código e do ID da verificação.Swift
let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationID, verificationCode: verificationCode )
Objective-C
FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID verificationCode:userInput];
- Faça o login do usuário com o objeto
FIRPhoneAuthCredential
:Swift
Auth.auth().signIn(with: credential) { authResult, error in if let error = error { let authError = error as NSError if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue { // The user is a multi-factor user. Second factor challenge is required. let resolver = authError .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver var displayNameString = "" for tmpFactorInfo in resolver.hints { displayNameString += tmpFactorInfo.displayName ?? "" displayNameString += " " } self.showTextInputPrompt( withMessage: "Select factor to sign in\n\(displayNameString)", completionBlock: { userPressedOK, displayName in var selectedHint: PhoneMultiFactorInfo? for tmpFactorInfo in resolver.hints { if displayName == tmpFactorInfo.displayName { selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo } } PhoneAuthProvider.provider() .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil, multiFactorSession: resolver .session) { verificationID, error in if error != nil { print( "Multi factor start sign in failed. Error: \(error.debugDescription)" ) } else { self.showTextInputPrompt( withMessage: "Verification code for \(selectedHint?.displayName ?? "")", completionBlock: { userPressedOK, verificationCode in let credential: PhoneAuthCredential? = PhoneAuthProvider.provider() .credential(withVerificationID: verificationID!, verificationCode: verificationCode!) let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator .assertion(with: credential!) resolver.resolveSignIn(with: assertion!) { authResult, error in if error != nil { print( "Multi factor finanlize sign in failed. Error: \(error.debugDescription)" ) } else { self.navigationController?.popViewController(animated: true) } } } ) } } } ) } else { self.showMessagePrompt(error.localizedDescription) return } // ... return } // User is signed in // ... }
Objective-C
[[FIRAuth auth] signInWithCredential:credential completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) { FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; NSMutableString *displayNameString = [NSMutableString string]; for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { [displayNameString appendString:tmpFactorInfo.displayName]; [displayNameString appendString:@" "]; } [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString] completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) { FIRPhoneMultiFactorInfo* selectedHint; for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { if ([displayName isEqualToString:tmpFactorInfo.displayName]) { selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo; } } [FIRPhoneAuthProvider.provider verifyPhoneNumberWithMultiFactorInfo:selectedHint UIDelegate:nil multiFactorSession:resolver.session completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; } else { [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName] completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) { FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID verificationCode:verificationCode]; FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { if (error) { [self showMessagePrompt:error.localizedDescription]; } else { NSLog(@"Multi factor finanlize sign in succeeded."); } }]; }]; } }]; }]; } else if (error) { // ... return; } // User successfully signed in. Get user data from the FIRUser object if (authResult == nil) { return; } FIRUser *user = authResult.user; // ... }];
Testar com números de telefone fictícios
É possível configurar números de telefone fictícios para desenvolvimento no Console do Firebase. Com isso, você tem estes benefícios:
- A autenticação do número de telefone é feita sem consumir sua cota de uso.
- A autenticação do número de telefone é feita sem enviar uma mensagem SMS real.
- Testes consecutivos são executados com o mesmo número de telefone, sem que haja limitação. Isso minimiza o risco de rejeição durante o processo de revisão da App store, caso o revisor use o mesmo número de telefone para teste.
- O teste em ambientes de desenvolvimento é feito prontamente e sem esforço extra, como a capacidade de desenvolver um simulador do iOS ou um emulador do Android sem o Google Play Services.
- A criação de testes de integração é feita sem que haja bloqueio por verificações de segurança, que normalmente são aplicadas a números de telefone reais em um ambiente de produção.
Os números de telefone fictícios precisam atender a estes requisitos:
- Use números de telefone que sejam realmente fictícios e não existam. O Firebase Authentication não permite que você configure números de telefone existentes já usados por usuários reais como números de teste. Uma opção é usar números com prefixo 555 como números de telefone de teste dos EUA, por exemplo: +1 650-555-3434
- Os números de telefone precisam estar formatados corretamente para atender a restrições como o tamanho. Eles ainda passarão pela mesma validação dos números de usuários reais.
- Você pode adicionar até 10 números de telefone para desenvolvimento.
- Use números de telefone/códigos de teste difíceis de adivinhar e altere-os com frequência.
Criar números de telefone e códigos de verificação fictícios
- No Console do Firebase, abra a seção Autenticação.
- Na guia Método de login, ative o provedor de telefonia, se ainda não fez isso.
- Abra o menu suspenso Números de telefone para testes.
- Informe o número de telefone que você quer testar, por exemplo: +1 650-555-3434.
- Informe o código de verificação de seis dígitos para esse número específico, por exemplo: 654321.
- Adicione o número. Se precisar, exclua o número de telefone e o código. Basta passar o cursor sobre a linha correspondente e clicar no ícone da lixeira.
Teste manual
Você pode começar a usar um número de telefone fictício diretamente no seu aplicativo. Dessa maneira, você pode executar testes manuais durante as etapas de desenvolvimento sem problemas de cotas ou limitações. Também é possível testar diretamente, a partir de um simulador do iOS ou emulador do Android, sem a instalação do Google Play Services.
Quando você fornece o número de telefone fictício e envia o código de verificação, nenhum SMS real é enviado. Em vez disso, é necessário informar o código de verificação configurado anteriormente para concluir o login.
Quando o login é concluído, um usuário do Firebase é criado com aquele número de telefone. Este usuário tem o mesmo comportamento e propriedades de um usuário com um número de telefone real e pode acessar o Realtime Database/Cloud Firestore e outros serviços da mesma forma. O token de ID gerado durante esse processo tem a mesma assinatura de um usuário com um número de telefone real.
Outra opção é definir um papel de teste por meio de declarações personalizadas nesses usuários para diferenciá-los como usuários falsos, se você quiser restringir ainda mais o acesso.
Teste de integração
Além do teste manual, o Firebase Authentication conta com APIs para ajudar a escrever testes de integração para o teste de autenticação por telefone. Essas APIs desativam a verificação de apps removendo o requisito reCAPTCHA na Web e em notificações push silenciosas no iOS. Isso possibilita o teste de automação nesses fluxos e facilita a implementação. Além disso, fica mais fácil testar os fluxos de verificação instantânea no Android.
No iOS, a configuração appVerificationDisabledForTesting
deve ser definida como
TRUE
antes que verifyPhoneNumber
seja chamado. O processamento não exige tokens de APN nem o envio de notificações push silenciosas em segundo plano, o que facilita o teste em um simulador. Além disso, o fluxo de fallback do reCAPTCHA é desativado.
Observe que, quando a verificação de apps está desativada, o login não é concluído se for usado um número de telefone não fictício. Só é possível usar números de telefone fictícios com essa API.
Swift
let phoneNumber = "+16505554567" // This test verification code is specified for the given test phone number in the developer console. let testVerificationCode = "123456" Auth.auth().settings.isAppVerificationDisabledForTesting = TRUE PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate:nil) { verificationID, error in if (error) { // Handles error self.handleError(error) return } let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID ?? "", verificationCode: testVerificationCode) Auth.auth().signInAndRetrieveData(with: credential) { authData, error in if (error) { // Handles error self.handleError(error) return } _user = authData.user }]; }];
Objective-C
NSString *phoneNumber = @"+16505554567"; // This test verification code is specified for the given test phone number in the developer console. NSString *testVerificationCode = @"123456"; [FIRAuth auth].settings.appVerificationDisabledForTesting = YES; [[FIRPhoneAuthProvider provider] verifyPhoneNumber:phoneNumber completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) { if (error) { // Handles error [self handleError:error]; return; } FIRAuthCredential *credential = [FIRPhoneAuthProvider credentialWithVerificationID:verificationID verificationCode:testVerificationCode]; [FIRAuth auth] signInWithAndRetrieveDataWithCredential:credential completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { if (error) { // Handles error [self handleError:error]; return; } _user = user; }]; }];
Apêndice: como usar o login com número de telefone sem swizzling
O Firebase Authentication usa o método swizzling para receber automaticamente o token de APNs do app, para manipular as notificações push silenciosas que o Firebase envia para seu app e para interceptar automaticamente o redirecionamento do esquema personalizado da página de verificação do reCAPTCHA durante a verificação.
Se preferir não usar o Swizzling, é possível desativá-lo. Adicione a sinalização FirebaseAppDelegateProxyEnabled
no arquivo Info.plist do seu aplicativo e a defina como NO
. Observe que, ao definir essa sinalização como NO
, o swizzling também será desativado em outros produtos do Firebase, incluindo o Firebase Cloud Messaging.
Se você desativar o swizzling, será necessário transmitir explicitamente para o Firebase Authentication o token de APNs do dispositivo, as notificações push e o URL de redirecionamento do esquema personalizado.
Se você estiver criando um aplicativo SwiftUI, também precisará transmitir explicitamente esses itens para o Firebase Authentication.
Para conseguir o token de APNs do dispositivo, implemente o
método
application(_:didRegisterForRemoteNotificationsWithDeviceToken:)
e transmita o token do dispositivo para o método setAPNSToken(_:type:)
do Auth
.
Swift
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Pass device token to auth Auth.auth().setAPNSToken(deviceToken, type: .prod) // Further handling of the device token if needed by the app // ... }
Objective-C
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // Pass device token to auth. [[FIRAuth auth] setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeProd]; // Further handling of the device token if needed by the app. }
Para processar notificações push, no método
application(_:didReceiveRemoteNotification:fetchCompletionHandler:):
, verifique se há notificações relacionadas ao Firebase Authentication chamando o método canHandleNotification(_:)
do Auth
.
Swift
func application(_ application: UIApplication, didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if Auth.auth().canHandleNotification(notification) { completionHandler(.noData) return } // This notification is not auth related; it should be handled separately. }
Objective-C
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // Pass notification to auth and check if they can handle it. if ([[FIRAuth auth] canHandleNotification:notification]) { completionHandler(UIBackgroundFetchResultNoData); return; } // This notification is not auth related; it should be handled separately. }
Para processar o URL de redirecionamento do esquema personalizado, implemente o
método application(_:open:options:)
e, neles, transmita o URL para o método canHandleURL(_:)
do Auth
.
Swift
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { if Auth.auth().canHandle(url) { return true } // URL not auth related; it should be handled separately. }
Objective-C
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } // URL not auth related; it should be handled separately. }
Se você estiver usando a SwiftUI ou o UISceneDelegate
, para controlar o URL de redirecionamento, implemente o método scene(_:openURLContexts:)
e, neles, transmita o URL para o método canHandleURL(_:)
do Auth
.
Swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { for urlContext in URLContexts { let url = urlContext.url Auth.auth().canHandle(url) } // URL not auth related; it should be handled separately. }
Objective-C
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.url]; // URL not auth related; it should be handled separately. } }
Próximas etapas
Depois que um usuário faz login pela primeira vez, uma nova conta de usuário é criada e vinculada às credenciais, que podem ser o número do telefone, o nome de usuário e a senha ou as informações do provedor de autenticação. Essa nova conta é armazenada como parte do projeto do Firebase e pode ser usada para identificar um usuário em todos os apps do projeto, seja qual for o método de login utilizado.
-
É possível receber as informações básicas de perfil do usuário do objeto
User
nos seus apps. Consulte Gerenciar usuários. Nas Regras de segurança do Firebase Realtime Database e do Cloud Storage, é possível acessar na variável
auth
o ID exclusivo do usuário que fez login e usar esse ID para controlar quais dados uma pessoa pode acessar.
Os usuários podem fazer login no app usando vários provedores de autenticação. Basta vincular as credenciais desses provedores a uma conta de usuário.
Para desconectar um usuário, chame
signOut:
.
Swift
let firebaseAuth = Auth.auth() do { try firebaseAuth.signOut() } catch let signOutError as NSError { print("Error signing out: %@", signOutError) }
Objective-C
NSError *signOutError; BOOL status = [[FIRAuth auth] signOut:&signOutError]; if (!status) { NSLog(@"Error signing out: %@", signOutError); return; }
Adicione também o código de tratamento de erros para todo o intervalo de erros de autenticação. Consulte Tratamento de erros.