Autenticando com a conta Google no Android utilizando o Firebase Authentication
Neste artigo veremos como autenticar usuários com o Firebase Authentication por meio do provedor da Google em Apps Android.
Pré-requisitos
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 aqui 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. Ao rodar, deve apresentar uma tela similar a esta:
Caso fique 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:
Adicionando o provedor do Google
O Firebase Authentication permite a autenticação através de vários provedores, neste projeto, utilizamos o provedor de e-mail e senha. Isso significa que para usar outro provedor, é necessário realizar os mesmos passos:
- Adicionar a dependência do play services:
implementation 'com.google.android.gms:play-services-auth:18.1.0'
- Adicionar a impressão digital SHA-1 no App:
Para pegar a impressão digital (fingerprint), você pode considerar qualquer uma das alternativas da documentação. Dentre elas, costumo usar a task do gradle signingReport
que pode ser executada diretamente no projeto, o resultado é similar a este:
> Task :app:signingReport
Variant: debug
Config: debug
Store: ~/.android/debug.keystore
Alias: AndroidDebugKey
MD5: A5:88:41:04:8D:06:71:6D:FE:33:76:87:AC:AD:19:23
SHA1: A7:89:E5:05:C8:17:A1:22:EA:90:6E:A6:EA:A3:D4:8B:3A:30:AB:18
SHA-256: 05:A2:2C:35:EE:F2:51:23:72:4D:72:67:A5:6C:8C:58:22:2A:00:D6:DB:F6:45:D5:C1:82:D2:80:A4:69:A8:FE
Valid until: Wednesday, August 10, 2044
- Habilitar o provedor do Google no console do Firebase
Observe que no campo de e-mail de suporte do projeto, você precisa colocar o seu, nesse caso usei um e-mail que criei para o projeto do Firebase.
Com todos os passos realizados, podemos implementar o código para realizar a autenticação.
Adicionando o botão de autenticação com o Google
Ao adicionar provedores como Google, Facebook ou outros serviços que oferecem a autenticação por meio do protocolo oAuth2, precisamos seguir alguns padrões na interface de usuário.
No caso da autenticação do Google, temos esse botão específico para indicar que a autenticação será feita a partir dele:
Podemos adicionar esse botão a partir desta tag dentro ConstraintLayout
do login.xml:
<com.google.android.gms.common.SignInButton
android:id="@+id/login_botao_signin_google"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/login_botao_cadastrar_usuario" />
Mesmo tendo todo o padrão, podemos mudar o tamanho do botão, porém, precisamos seguir as limitações da Google, que oferece constantes via código fonte para isso:
package br.com.alura.aluraesporte.ui.fragment
// imports
import com.google.android.gms.common.SignInButton
class LoginFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
estadoAppViewModel.temComponentes = ComponentesVisuais()
configuraBotaoLogin()
configuraBotaoCadastro()
login_botao_signin_google.setSize(SignInButton.SIZE_STANDARD)
}
// membros
}
Sendo o padrão o SIZE_STANDARD
e as demais opções, SIZE_WIDE
ou SIZE_ICON
.
Se preferir o padrão, não é necessário o
SIZE_STANDARD
Caso o padrão não seja seguido, provavelmente, o seu App não será publicado por não seguir o padrão da integração com o serviço da Google.
Configurando as opções de inscrição da Google
Ao autenticar com o provedor da Google, precisamos configurar as opções de inscrições para definir como será feita a autenticação como, por exemplo, exigindo o token id e e-mail do usuário ou usuária:
login_botao_signin_google.setOnClickListener {
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
}
Abrindo a tela de autenticação com o Google
Com o GoogleSignInOptions
configurado, podemos criar o cliente da Google (GoogleSignInClient
) e abrir a tela de autenticação a partir de uma Intent
:
login_botao_signin_google.setOnClickListener {
val gso =
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
val cliente = GoogleSignIn.getClient(requireContext(), gso)
startActivityForResult(cliente.signInIntent, RC_SIGN_IN_GOOGLE)
}
RC_SIGN_IN_GOOGLE
é uma constante que pode ser qualquer valor inteiro, por exemplo, o1
.
Ao rodar o projeto e clicar no botão da Google, temos o seguinte resultado sem uma conta configurada:
Ao configurar uma conta da Google, o fluxo de autenticação apenas solicita a permissão de autorização das informações da conta:
Ao finalizar o cadastro ou clicar na sua conta, a autenticação é feita, porém, isso não é o suficiente para integrar com o serviço do Firebase Authentication.
Observe que fazemos a chamada a partir de uma Intent
que espera um resultado, portanto, precisamos também sobrescrever o método onActivityResult()
.
Obtendo o resultado da autenticação pela Intent
A implementação é similar às demais Intent
s, verificamos se o código de resultado é sucesso e se o código da requisição é o mesmo que enviamos ao iniciar a Intent
:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == RC_SIGN_IN_GOOGLE) {
}
}
Então teremos o resultado e veremos o que acontece ao tentar autenticar pelo botão da Google:
if (resultCode == RESULT_OK && requestCode == RC_SIGN_IN_GOOGLE) {
val contaGoogle = GoogleSignIn.getSignedInAccountFromIntent(data).result
Log.i(TAG, "onActivityResult: conta google autenticada $contaGoogle")
}
O resultado deve ser similar a este:
2020-07-31 15:59:08.790 3380-3380/br.com.alura.aluraesporte I/ContentValues: onActivityResult: conta google autenticada com.google.android.gms.auth.api.signin.GoogleSignInAccount@9aba2bbc
Com essa resposta, conseguimos integrar o comportamento de inscrição com o provedor da Google! Porém, é necessário fazer mais um passo para vincular a conta da Google com o Firebase Authentication.
Vinculando a conta da Google com o Firebase Authentication
O vínculo da conta do Google com o Firebase Authentication precisa das credenciais do provedor da Google.
Para isso podemos usar o método estático getCredentials()
da classe GoogleAuthProvider
que recebe o um idToken
e um accessToken
da conta do Google.
contaGoogle?.let { conta ->
val credencial = GoogleAuthProvider.getCredential(conta.idToken, null)
}
Observe que apenas o
idToken
é o necessário para obter a credencial.
Em seguida, precisaremos de uma instância de FirebaseAuth
para fazer o vínculo, para isso podemos utilizar o LoginViewModel
que tem acesso ao FirebaseAuthRepository
que, por sua vez, tem uma instância do FirebaseAuth
:
class FirebaseAuthRepository(private val firebaseAuth: FirebaseAuth) {
//membros
fun vinculaContaGoogle(credencial: AuthCredential) : LiveData<Resource<Boolean>> {
val liveData = MutableLiveData<Resource<Boolean>>()
firebaseAuth.signInWithCredential(credencial)
.addOnSuccessListener {
liveData.value = Resource(true)
}
.addOnFailureListener {
liveData.value = Resource(false, "Falha ao vincular conta com a Google")
}
return liveData
}
}
Note que a implementação é similar aos demais comportamentos feitos com o FirebaseAuth
, ou seja, precisamos apenas delegar o retorno do LiveData
para quem estiver chamando, seja no ViewModel
:
class LoginViewModel(
private val firebaseAuthRepository: FirebaseAuthRepository
) : ViewModel() {
//membros
fun vinculaContaGoogle(credencial: AuthCredential): LiveData<Resource<Boolean>> =
firebaseAuthRepository.vinculaContaGoogle(credencial)
}
Como, também, no Fragment de login para tomar a devida ação no caso de falha ou sucesso:
contaGoogle?.let { conta ->
val credencial = GoogleAuthProvider.getCredential(conta.idToken, null)
viewModel.vinculaContaGoogle(credencial)
.observe(viewLifecycleOwner, Observer {
it?.let { recurso ->
if(recurso.dado){
vaiParaListaProdutos()
} else {
val mensagem = recurso.erro ?: "Falha ao vincular com a conta Google"
view?.snackBar(mensagem)
}
}
})
}
Ao executar novamente, temos o seguinte resultado ao tentar logar com a conta Google:
Por ter logado uma vez, a configuração de autorização é armazenada no App. Portanto, para apresentar novamente o mesmo dialog que solicita a autenticação com uma conta Google, é necessário realizar alguns passos a mais.
Deslogando a conta Google
Para que a pessoa consiga autenticar com uma outra conta Google, podemos apenas apagar os dados do App ou reinstalá-lo. Porém, para uma melhor experiência de usuário(a), podemos realizar essa tarefa ao deslogar com o FirebaseAuth
.
O processo de deslogar com a conta da Google exige uma instância de GoogleSignInClient
, sendo assim, podemos criar uma extension function que permite ter acesso a essa referência para quem tem acesso a um Context
:
package br.com.alura.aluraesporte.extensions
import android.content.Context
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
fun Context.googleSignInClient(): GoogleSignInClient {
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(br.com.alura.aluraesporte.R.string.default_web_client_id))
.requestEmail()
.build()
return GoogleSignIn.getClient(this, gso)
}
Então podemos agora apenas usar essa extension para ter acesso ao cliente do Google tanto no Fragment de Login:
login_botao_signin_google.setOnClickListener {
val cliente = requireContext().googleSignInClient()
startActivityForResult(cliente.signInIntent, RC_SIGN_IN_GOOGLE)
}
Como, também, no BaseFragment
,para que seja possível realizar o processo de sign out com a conta do Google:
abstract class BaseFragment : Fragment() {
//membros
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.menu_principal_deslogar){
loginViewModel.desloga()
requireContext().googleSignInClient().signOut()
vaiParaLogin()
}
return super.onOptionsItemSelected(item)
}
}
Então, basta deslogar do App e logar novamente com uma conta Google:
Pronto! Conseguimos integrar o mecanismo de autenticação do Firebase Authentication utilizando o provedor do Google :)
Caso queira conferir o código desenvolvido durante o artigo, você pode conferir as mudanças a partir deste commit ou navegar pelo repositório do GitHub.
E se você quer começar ou se aprofundar em Android, os cursos da Formação Android da Alura vão ajudar você a nortear seus estudos nesta área.