1. Antes de começar
Aprenda a implementar o recurso Fazer login com o Google no Android usando o Credential Manager.
Pré-requisitos
- Conhecimentos básicos de Kotlin para desenvolvimento Android.
- Conhecimentos básicos do Jetpack Compose (saiba mais).
O que você vai aprender
- Criar um projeto do Google Cloud e clientes OAuth.
- Implementar o fluxo de login da página inferior.
- Implementar o fluxo de login do botão explícito.
O que é necessário
- Android Studio instalado.
- Um computador que atenda aos requisitos do sistema do Android Studio e do emulador .
- Java Development Kit (JDK) instalado.
2. Criar um projeto do Android Studio
Para começar, crie um novo projeto no Android Studio:
- Abra o Android Studio e clique em New Project.

- Selecione Phone and Tablet > Empty Activity e clique em Next.

- Configure as definições do projeto:
- Nome: escolha um nome para o projeto.
- Nome do pacote: use o padrão ou escolha o seu.
- Minimum SDK: selecione a versão estável mais recente ou > Android 14.

- Clique em Finish e aguarde a conclusão da criação inicial do projeto.

3. Configurar seu projeto do Google Cloud
Criar um projeto do Google Cloud
- Acesse o Console do Google Cloud e selecione ou crie um projeto.

- Navegue até APIs & Services > tela de permissão do OAuth.

- Clique em Get started e preencha os campos obrigatórios:
- Nome do app: use o nome do seu app Android.
- User Support Email: selecione sua Conta do Google.
- Audience: selecione External.
- Informações de contato: insira seu endereço de e-mail.

- Analise a Google API Services: User Data Policy e clique em Create.

Configurar clientes OAuth
Você precisa criar um cliente da Web e um cliente Android no Console do Google Cloud para receber os IDs do cliente para autenticação.
- Cliente Android: protege as solicitações verificando o nome do pacote e a assinatura SHA-1 do app.
- Cliente da Web: atua como o cliente de back-end do serviço Fazer login com o Google.
Criar um cliente OAuth 2.0 do Android
- Na página Clients, clique em Create Client e selecione Android como o Application type.

- Insira o nome do pacote do seu app (correspondente à linha 1 do
MainActivity.kt). - Gere sua assinatura SHA-1. Abra o terminal do Android Studio e execute:macOS/Linux:
Windows:keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
keytool -list -v -keystore "C:\Users\USERNAME\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
- Copie a impressão digital SHA-1 da saída do comando, cole-a no campo SHA-1 fingerprint no console e clique em Create.

Criar um cliente OAuth 2.0 da Web
- Clique em Create Client novamente e selecione Web Application como o Application type.
- Nomeie o cliente da Web, deixe os campos de URL/origens em branco e clique em Create.

- Copie o Client ID gerado na caixa de diálogo de confirmação. Você vai usar isso no seu código Kotlin.

4. Configurar um dispositivo virtual Android
Para testar o app, você pode usar um dispositivo Android físico ou um dispositivo virtual Android (AVD).
Criar e executar o AVD
- No Android Studio, abra o Gerenciador de dispositivos, clique em Criar dispositivo virtual (ou no ícone +) e selecione Medium Phone.
- Selecione a versão estável mais recente como a imagem do sistema e clique em Finish.
- Inicie o emulador clicando no ícone Play/Run ao lado do dispositivo.

Fazer login em uma Conta do Google no dispositivo
- No emulador, abra o app Settings e acesse Google.
- Clique em Fazer login na sua Conta do Google e siga as instruções.

5. Adicionar dependências
Adicione as bibliotecas necessárias para autenticação e integração do ID do Google ao seu projeto:
- Acesse File > Project Structure > Dependencies > app.
- Clique em + > Library Dependency , pesquise
com.google.android.libraries.identity.googleid:googleide selecione a versão mais recente (por exemplo,1.1.1). - Clique em + > Library Dependency novamente, pesquise
play-services-authe selecione a biblioteca com o ID do grupocom.google.android.gms. - Clique em OK para aplicar as mudanças e sincronizar seu projeto.

6. Implementar o fluxo da página inferior

O fluxo da página inferior aproveita a API Credential Manager para oferecer uma maneira simplificada para os usuários fazerem login no seu app usando as Contas do Google no Android. Projetado para velocidade e conveniência, especialmente para usuários recorrentes, esse fluxo precisa ser acionado na inicialização do app.
Criar a solicitação de login
- Para começar, abra
MainActivity.kte remova as funçõesGreeting()eGreetingPreview()padrão. - Adicione as seguintes declarações de importação após as existentes, começando na linha 3:
import android.content.Context import android.os.Build import android.util.Log import android.widget.Toast import androidx.annotation.RequiresApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.credentials.CredentialManager import androidx.credentials.CustomCredential import androidx.credentials.GetCredentialRequest import androidx.credentials.exceptions.GetCredentialCancellationException import androidx.credentials.exceptions.GetCredentialCustomException import androidx.credentials.exceptions.GetCredentialException import androidx.credentials.exceptions.NoCredentialException import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException import java.security.SecureRandom import java.util.Base64 import kotlinx.coroutines.delay import kotlinx.coroutines.launch const val TAG = "MainActivity" - Adicione esta função combinável abaixo da classe
MainActivityno arquivoMainActivity.kt:@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @Composable fun BottomSheet(webClientId: String) { val context = LocalContext.current // LaunchedEffect is used to run a suspend function when the composable is first launched. LaunchedEffect(Unit) { // Create a Google ID option with filtering by authorized accounts enabled. val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(true) .setServerClientId(webClientId) .setNonce(generateSecureRandomNonce()) .build() // Create a credential request with the Google ID option. val request: GetCredentialRequest = GetCredentialRequest.Builder() .addCredentialOption(googleIdOption) .build() // Attempt to sign in with the created request using an authorized account val e = signIn(request, context) // If the sign-in fails with NoCredentialException, there are no authorized accounts. // In this case, we attempt to sign in again with filtering disabled. if (e is NoCredentialException) { val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(false) .setServerClientId(webClientId) .setNonce(generateSecureRandomNonce()) .build() val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder() .addCredentialOption(googleIdOptionFalse) .build() //We will build out this function in a moment signIn(requestFalse, context) } } } //This function is used to generate a secure nonce to pass in with our request fun generateSecureRandomNonce(byteLength: Int = 32): String { val randomBytes = ByteArray(byteLength) SecureRandom.getInstanceStrong().nextBytes(randomBytes) return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes) }
Detalhamento do código
LaunchedEffect(Unit): aciona o fluxo de login imediatamente quando o elemento combinável é exibido pela primeira vez.GetGoogleIdOption.Builder(): configura a solicitação de token de ID do Google.setFilterByAuthorizedAccounts(true): primeiro tenta um login silencioso filtrando as contas que o usuário já autorizou para esse app, minimizando o atrito para usuários recorrentes.setNonce(...): transmite um valor de uso único aleatório seguro gerado para cada solicitação porgenerateSecureRandomNonce()para evitar ataques de repetição.
signIn(request, context): executa a solicitação. Se falhar com umaNoCredentialException(ou seja, nenhuma conta autorizada anteriormente existe), o fluxo volta parasetFilterByAuthorizedAccounts(false)para permitir que o usuário selecione qualquer Conta do Google conectada no dispositivo.
Fazer a solicitação de login
Com a solicitação de login criada, você pode usar o Gerenciador de credenciais para concluir o processo de login. Crie uma função chamada signIn que executa a solicitação e processa exceções comuns que podem ocorrer.
Adicione essa função abaixo da função BottomSheet no arquivo MainActivity.kt:
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
val credentialManager = CredentialManager.create(context)
val failureMessage = "Sign in failed!"
//using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
//on the initial running of our app
delay(250)
return try {
// The getCredential is called to request a credential from Credential Manager.
val result = credentialManager.getCredential(
request = request,
context = context,
)
Log.i(TAG, result.toString())
val credential = result.credential
if (credential is CustomCredential &&
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
}
Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
Log.i(TAG, "(☞゚ヮ゚)☞ Sign in Successful! ☜(゚ヮ゚☜)")
null
} catch (e: GoogleIdTokenParsingException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
e
} catch (e: NoCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": No credentials found", e)
e
} catch (e: GetCredentialCancellationException) {
Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
e
} catch (e: GetCredentialCustomException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
e
} catch (e: GetCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Failure getting credentials", e)
e
}
}
Detalhamento do código
credentialManager.getCredential(...): chama a API Credential Manager para exibir a página inferior ou a caixa de diálogo do seletor de contas do sistema.delay(250): pausa brevemente para evitar uma disputa quando a página inferior é acionada imediatamente na inicialização do app antes que o serviço do Credential Manager termine de ser inicializado.- Processamento de exceções: captura e registra erros de credenciais comuns (por exemplo, cancelamento, credenciais ausentes ou problemas de análise de token) e fornece feedback do usuário usando avisos.
Acionar o fluxo da página inferior
Atualize sua classe MainActivity para chamar BottomSheet() na inicialização. Substitua YOUR_CLIENT_ID_HERE pelo ID do cliente do aplicativo da Web:
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
//This will trigger on launch
BottomSheet(webClientId)
}
}
}
}
}
Salve o projeto (File > Save) e execute o aplicativo:
- Pressione o botão de execução:

- Quando o app for iniciado no emulador, a página inferior de login vai aparecer. Clique em Continue para testar o fluxo.

- Uma notificação de aviso vai aparecer, confirmando que o login foi concluído.

7. Implementar o fluxo do botão

O fluxo do botão oferece uma opção explícita para os usuários fazerem login ou se inscreverem. O uso de branding padrão garante uma experiência consistente. Use recursos pré-aprovados que estejam em conformidade com as diretrizes de branding do recurso Fazer login com o Google.
Adicionar o ícone da marca
- Faça o download dos recursos da marca aqui e extraia o ZIP.
- Copie
signin-assets/Android/png@2x/neutral/android_neutral_sq_SI@2x.png. - No Android Studio, cole o arquivo na pasta res > drawable , renomeie-o para
siwg_button.pnge clique em OK.
Código do fluxo do botão
Esse fluxo reutiliza a mesma função auxiliar signIn, mas transmite um GetSignInWithGoogleOption em vez de GetGoogleIdOption. Ao contrário do fluxo da página inferior, o fluxo do botão explícito não pré-filtra nem solicita automaticamente credenciais ou chaves de acesso armazenadas. Cole essa função combinável abaixo da função BottomSheet:
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
.Builder(serverClientId = webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build()
coroutineScope.launch {
signIn(request, context)
}
}
Image(
painter = painterResource(id = R.drawable.siwg_button),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
.clickable(enabled = true, onClick = onClick)
)
}
Detalhamento do código
GetSignInWithGoogleOption: ao contrário do fluxo da página inferior, o fluxo do botão explícito usa essa opção para pedir aos usuários que selecionem a Conta do Google sem filtragem automática.coroutineScope.launch: inicia uma corrotina para executar a funçãosignInde suspensão de forma assíncrona quando o botão é clicado.Image: exibe o drawablesiwg_buttonda marca e anexa um listener de cliques para acionar o fluxo.
Adicionar o botão ao layout da interface
Atualize o layout MainActivity para exibir a BottomSheet automática e a ButtonUI explícita alinhadas verticalmente:
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
//This will trigger on launch
BottomSheet(webClientId)
//This requires the user to press the button
ButtonUI(webClientId)
}
}
}
}
}
}
Testar o fluxo do botão
- Execute o app.
- Feche a página inferior inicial clicando fora da área da página.
- Clique no botão Fazer login com o Google para iniciar a caixa de diálogo de login e selecione sua conta.

- Verifique o resultado: confira o Logcat para confirmar a impressão do seu nome de usuário/endereço de e-mail.
8. Conclusão
Parabéns! Você implementou o recurso Fazer login com o Google usando o Gerenciador de credenciais do Android.
Outros recursos
- Documentação do Credential Manager do Android
- Guia de solução de problemas do Credential Manager
- Perguntas frequentes sobre o Credential Manager
- Suporte do Stack Overflow
Código completo de MainActivity.kt
Confira o código completo de MainActivity.kt para referência:
package com.example.example
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.example.example.ui.theme.ExampleTheme
import android.content.ContentValues.TAG
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.credentials.exceptions.GetCredentialException
import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.credentials.CredentialManager
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialCustomException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.GetCredentialRequest
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import java.security.SecureRandom
import java.util.Base64
import kotlinx.coroutines.CoroutineScope
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
//This will trigger on launch
BottomSheet(webClientId)
//This requires the user to press the button
ButtonUI(webClientId)
}
}
}
}
}
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun BottomSheet(webClientId: String) {
val context = LocalContext.current
// LaunchedEffect is used to run a suspend function when the composable is first launched.
LaunchedEffect(Unit) {
// Create a Google ID option with filtering by authorized accounts enabled.
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(webClientId)
.setNonce(generateSecureRandomNonce())
.build()
// Create a credential request with the Google ID option.
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
// Attempt to sign in with the created request using an authorized account
val e = signIn(request, context)
// If the sign-in fails with NoCredentialException, there are no authorized accounts.
// In this case, we attempt to sign in again with filtering disabled.
if (e is NoCredentialException) {
val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOptionFalse)
.build()
signIn(requestFalse, context)
}
}
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
.Builder(serverClientId = webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build()
coroutineScope.launch {
signIn(request, context)
}
}
Image(
painter = painterResource(id = R.drawable.siwg_button),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
.clickable(onClick = onClick)
)
}
fun generateSecureRandomNonce(byteLength: Int = 32): String {
val randomBytes = ByteArray(byteLength)
SecureRandom.getInstanceStrong().nextBytes(randomBytes)
return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
val credentialManager = CredentialManager.create(context)
val failureMessage = "Sign in failed!"
//using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
//on the initial running of our app
delay(250)
return try {
// The getCredential is called to request a credential from Credential Manager.
val result = credentialManager.getCredential(
request = request,
context = context,
)
Log.i(TAG, result.toString())
val credential = result.credential
if (credential is CustomCredential &&
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
}
Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
Log.i(TAG, "(☞゚ヮ゚)☞ Sign in Successful! ☜(゚ヮ゚☜)")
null
} catch (e: GoogleIdTokenParsingException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
e
} catch (e: NoCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": No credentials found", e)
e
} catch (e: GetCredentialCancellationException) {
Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
e
} catch (e: GetCredentialCustomException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
e
} catch (e: GetCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Failure getting credentials", e)
e
}
}