Autenticação com FirebaseUI no Android
O que é FirebaseUI?
Antes de configurar ou implementar código, é muito importante entender o que é o FirebaseUI e a sua proposta. Basicamente, é uma biblioteca do Firebase com a proposta de facilitar a implementação do Firebase Authentication.
Em outras palavras, o FirebaseUI implementa todo o fluxo de tela e lógica de autenticação, isso significa que não há a necessidade de criar um layout próprio e implementar todo o fluxo assim como fazemos no SDK do Firebase Authentication.
Isso também significa que temos restrições na personalização das telas e fluxo utilizando o FirebaseUI e, caso você tenha interesse em ter uma experiência diferente para o seu usuário, você não deve considerar o uso do FirebaseUI.
Tendo consciência das vantagens e desvantagens, agora podemos começar a configuração do FirebaseUI.
Você pode conferir mais detalhes sobre a capacidade do FirebaseUI na página de introdução oficial do Firebase.
Pré-requisitos
Neste artigo usaremos o projeto desenvolvido no curso de Firebase Authentication com Android. Se você já tem o projeto, fique à vontade em reutilizá-lo, caso contrário, você pode baixá-lo ou acessar o código fonte via GitHub.
O projeto fornecido não acompanha o arquivo de integração com o Firebase, o google-services.json. Isso significa que ao rodar o App, o Android Studio vai indicar a ausência do arquivo e não vai executar.
Para resolver o problema, você precisa ter ou registrar um App para Android no console do Firebase com o pacote br.com.alura.aluraesporte. Então, basta apenas baixar o arquivo e adicionar dentro do módulo app do projeto.
Caso esteja com dúvida de como fazer a configuração, confira a primeira aula do curso ou consulte a página da documentação do Firebase que explica o passo-a-passo.
Ao rodar o App deve apresentar a seguinte tela:
É importante ressaltar que iremos explorar a autenticação por meio de e-mail e senha, portanto, é necessário configurar o console do projeto do Firebase. Para isso você pode acessar o menu Authentication > Sign-in method para que permita esse tipo de autenticação:
Após preparar todo ambiente, já somos capazes de iniciar a implementação do FirebaseUI no projeto Android.
Adicionando o FirebaseUI no projeto
Como primeiro passo, precisamos ter acesso ao FirebaseUI no projeto. Para isso adicionamos as seguintes dependências:
dependencies {
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'
}
Como a própria documentação indica, caso queira fazer a autenticação com o Twitter ou Facebook, é necessário adicionar libs separadas também:
dependencies {
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'
// Necessário somente para o login com Facebook
// A versão mais atual do Facebook SDK pode ser encontrada aqui: https://goo.gl/Ce5L94
implementation 'com.facebook.android:facebook-android-sdk:4.x'
// Necessário somente para o login com Twitter
// A versão mais atual do Twitter SDK pode ser encontrada aqui: https://goo.gl/E5wZvQ
implementation 'com.twitter.sdk.android:twitter-core:3.x'
}
Após a adição, basta apenas sincronizar o App para ter o acesso aos componentes do FirebaseUI.
Adicionando os botões de autenticação
Agora que temos o FirebaseUI, precisamos de uma instância de AuthUI
que será responsável em controlar todo o fluxo. Para isso, no onViewCreated()
do LoginFragment
, podemos pegar a instância via método estático da classe AuthUI
:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//restante do código
val authUi = AuthUI.getInstance()
}
Em seguida precisamos montar a Intent
que vai ficar responsável em abrir as telas do fluxo do FirebaseUI, fazemos isso com o método createSignInIntentBuilder()
e adicionamos os provedores disponíveis durante o processo de construção:
val authUi = AuthUI.getInstance()
val intent = authUi.createSignInIntentBuilder()
.setAvailableProviders(listOf(AuthUI.IdpConfig.EmailBuilder().build()))
.build()
Note que ele recebe uma lista, ou seja, é possível enviar mais de um provedor de uma vez.
Então precisamos inicializar a Intent
, porém, a inicialização da Intent
é a partir do método startActivityForResult()
, pois ao finalizar a Activity inicializada, precisamos lidar com o retorno:
val authUi = AuthUI.getInstance()
val intent = authUi.createSignInIntentBuilder()
.setAvailableProviders(listOf(AuthUI.IdpConfig.EmailBuilder().build()))
.build()
startActivityForResult(intent, RC_SIGN_IN)
RC_SIGN_IN
é uma constante que identifica a requisição feita pelaIntent
, você pode colocar o valorInt
que preferir, como por exemplo, o1
Então podemos rodar o App e conferir o que acontece:
Note que não temos mais acesso à nossa tela de Login que fizemos anteriormente!
Testando o fluxo de autenticação e cadastro via e-mail e senha
Apenas com essa implementação temos acesso ao fluxo completo de autenticação via e-mail e senha utilizando o Firebase Authentication como, por exemplo, o cadastro de um usuário inexistente:
Ou a autenticação quando o usuário já existe:
Em ambos os casos, ao abrir o App novamente, que faz a verificação da existência de um usuário logado, entramos diretamente na tela inicial, a lista de produtos.
Para fazer as simulações, você pode deslogar do App :)
Entretanto, esse fluxo natural não acontece após cadastrar ou autenticar com o FirebaseUI... O motivo disso é o fato de que não estamos lidando com a resposta da inicialização da Activity, ou seja, precisamos sobrescrever o onActivityResult()
e implementar a devida verificação:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
vaiParaListaProdutos()
}
}
}
Nessa implementação basicamente identificamos a requisição que foi feita e se obtivermos um código de sucesso, então redirecionamos para a tela da lista de produtos que já faz a verificação da existência de usuário:
Observe que agora o App mantém o comportamento natural durante a autenticação ou no cadastro:
E agora temos uma implementação do fluxo de cadastro e autenticação com e-mail e senha utilizando o FirebaseUI, simples né?
Lidando com os possíveis problemas
Na nossa primeira implementação, simulamos apenas os 'caminhos felizes', quando tudo ocorre como o esperado, e não notamos nenhum comportamento estranho. Porém, em situações que temos uma falha o App volta novamente para a tela de login e sem nenhum feedback para o usuário ou usuária, um comportamento bastante estranho.
Para lidarmos com isso, podemos verificar quando o resultado não é RESULT_OK
e identificar o possível problema com a resposta recebida:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
vaiParaListaProdutos()
} else {
val response = IdpResponse.fromResultIntent(data)
Log.e(TAG, "onActivityResult: falha ao autenticar", response?.error)
view?.snackBar("Falha ao autenticar")
}
}
}
TAG
é uma constante do tipoString
, você pode colocar o valor que preferir para identificar o log, geralmente eu costumo colocar no nome da classe.
Ao testar novamente a autenticação temos a identificação do problema visualmente:
E uma informação mais precisa via logcat:
2020-08-19 12:30:55.419 19460-19460/br.com.alura.aluraesporte E/LoginFragment: onActivityResult: falha ao autenticar
Nesse caso temos um erro nulo, o que indica que este usuário voltou da tela de autenticação segundo a amostra de código da documentação, inclusive, também é indicado que em casos que não é nulo, é possível verificar o código para identificar o erro:
val response = IdpResponse.fromResultIntent(data)
Adaptando a tela inicial com o FirebaseUI
Um outro ponto importante em relação ao uso do FirebaseUI, é que delegamos toda a responsabilidade de autenticação para ele, ou seja, não faz sentido manter a mesma tela de login que temos quando consideramos o seu uso.
Em outras palavras, é mais conveniente utilizar uma tela que apresente um botão para entrar no App e assim abrir o FirebaseUI:
Essa implementação pode ser feita de várias maneiras, considerando este projeto que utiliza o Navigation, são necessários os seguintes passos:
- Criar um novo destino (Fragment) para a tela de início;
- Implementar a tela (XML de layout);
- Configurar o listener do botão Entrar para abrir o componente do FirebaseUI;
- Migrar a sobrescrita do
onActivityResult()
da tela de login para a tela de início; - Configurar o controlador do navigation para acessar a lista de produtos quando a autenticação for sucedida;
- Configurar o Fragment base para redirecionar para a tela de início ao invés da tela de login quando o usuário não estiver autenticado ou quando for deslogado;
Considere esses passos como desafio e tente implementá-los ;)
Caso queira conferir como foi feita toda a implementação do artigo, confira este commit ou o código fonte no repositório do GitHub.
Para saber mais: Possibilidades com o FirebaseUI
A implementação do FirebaseUI desenvolvida durante o artigo é apenas uma parte das possibilidades que essa biblioteca oferece, ou seja, há muitas outras possibilidades e funcionalidades que podem ser exploradas. Caso tenha interesse em saber mais além do conteúdo introdutório visto na documentação, confira o README do projeto via GitHub.