Autenticando com a conta Google no Android utilizando o Firebase Authentication

Autenticando com a conta Google no Android utilizando o Firebase Authentication
Alex Felipe
Alex Felipe

Compartilhe

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:

página inicial de cadastro de email e senha Banner de divulgação da Imersão IA da Alura em colaboração com o Google. Mergulhe em Inteligência artificial com a Alura e o Google. Serão cinco aulas gratuitas para você aprender a usar IA na prática e desenvolver habilidades essenciais para o mercado de trabalho. Inscreva-se gratuitamente agora!

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:
página inicial para adicionar o aplicativo

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
página de habilitação 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:

página de login com email e senha

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, o 1.

Ao rodar o projeto e clicar no botão da Google, temos o seguinte resultado sem uma conta configurada:

gif de checagem de informação

Ao configurar uma conta da Google, o fluxo de autenticação apenas solicita a permissão de autorização das informações da conta:

gif para escolha da conta e autorização das informações

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 Intents, 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:

gif de entrada no aplicativo e aparecimento da conta logada

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:

gif de saída do app

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.

Alex Felipe
Alex Felipe

Alex é instrutor e desenvolvedor e possui experiência em Java, Kotlin, Android. Atualmente cria conteúdo no canal https://www.youtube.com/@AlexFelipeDev.

Veja outros artigos sobre Mobile