Integrate Credential Manager with SignIn with Google

Rajan Maurya
AndroidByteSensei
Published in
6 min readApr 20, 2024

Welcome! If you have landed on this blog it means you are researching to migrate from GoogleSignIn.getClient(context, GoogleSignInOptions) to CredentialManager.create(context) .

CredentialManager is callback Google API so you don’t need to implement any UI, Just call CredentialManager API and It will open the sign-in account bottom sheet the user gonna pick and we will get the callback.

Let’s do it using Jetpack Compose. we will implement step by step everything

  1. Setting Up Google APIs Console Project.
  2. Add CredentialManager Library in your project.
  3. Build Jetpack Compose to call the Google SignIn with CredentialManager.
  4. CredentialManager API implementation.

Let’s do it one by one

1. Setting Up Google APIs Console Project.

On the top left click onSelect a project , if you don’t have any project then create NEW PROJECT by clicking the top right of the dialog shown in the above screenshot.

  • Navigate to OAuth consent screen page and make sure all of the information is complete and accurate. In particular, make sure you have specified the URLs of your app’s privacy policy and terms of service.

Now create the OAuth consent screen You can choose any Internal or External according to your use case. If you are building an app to distribute to the public then choose External

Fill in all the information and complete the OAuth consent.

  • Go to the Credential Page, click “Create Credentials,” and select OAuth Client ID

Click on + CREATE CREDENTIALS

Choose OAuth client ID

Create an OAuth Client ID for Android, providing necessary details such as application type, package name, and SHA-1 fingerprint etc.

Obtain the SHA-1 fingerprint(https://developers.google.com/android/guides/client-auth) using the provided command in your terminal

keytool -list -v \
-alias <your-key-name> -keystore <path-to-production-keystore>

keytool -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore

As you created Android client Id, do the same steps and create Web Client Id

  • Select application type Web application and fill Name fill. Click Create.
    You can leave the Authorized JavaScript Origins and Authorized redirect URIs fields blank

2. Add CredentialManager Library in your project.

Open app-level build.gradle and add dependency

dependencies {
// ... other dependencies
val credentials = "1.2.2"
val identity = "1.1.0"

implementation "androidx.credentials:credentials:${credentials}"
implementation "androidx.credentials:credentials-play-services-auth:${credentials}"
implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

Add proguard file in your proguard-rules.pro file to ensure proper handling of Credential Manager classes.

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}

3. Build Jetpack Compose to call the Google SignIn with CredentialManager.

@Composable
fun SignupMethodContentScreen(
showProgress: Boolean,
onSignupAsCustomer: () -> Unit,
) {
Box(
modifier = Modifier,
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(color = Color.White),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = stringResource(id = R.string.create_an_account)
)
OutlinedButton(
modifier = Modifier.padding(top = 48.dp),
onClick = {
onSignupAsCustomer.invoke()
},
border = BorderStroke(1.dp, Color.LightGray),
shape = RoundedCornerShape(4.dp),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.Black)
) {
Text(
text = stringResource(id = R.string.sign_up).uppercase(),
style = MaterialTheme.typography.labelMedium
)
}
}
if (showProgress) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 140.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(64.dp),
color = Color.Black,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
}
}
}
}

Now we have created a composable that will be called google sign in.

4. CredentialManager API implementation.

import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.credentials.ClearCredentialStateRequest
import androidx.credentials.CredentialManager
import androidx.credentials.CustomCredential
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.exceptions.GetCredentialException
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import kotlinx.coroutines.launch

@Composable
fun SocialSignupMethodContentScreen() {
SocialSignupMethodScreen()
}

@Composable
fun SocialSignupMethodScreen() {

val context = LocalContext.current
var showProgress by remember { mutableStateOf(false) }

val credentialManager = CredentialManager.create(context)
val coroutineScope = rememberCoroutineScope()
var showFilterByAuthorizedAccounts by rememberSaveable { mutableStateOf(true) }
var googleIdTokenCredential by remember { mutableStateOf<GoogleIdTokenCredential?>(null) }

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId("SERVER_CLIENT_ID")
.build()

val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()


fun signUpWithMifos() {
googleIdTokenCredential.signUpWithForm(context) {
coroutineScope.launch {
credentialManager.clearCredentialState(ClearCredentialStateRequest())
}
}
}

fun handleSignIn(result: GetCredentialResponse) {
// Handle the successfully returned credential.
when (val credential = result.credential) {
is CustomCredential -> {
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
try {
googleIdTokenCredential =
GoogleIdTokenCredential.createFrom(credential.data)
googleIdTokenCredential?.let {
signUpWithMifos()
} ?: {
Toast.makeText(context, Constants.GOOGLE_SIGN_IN_FAILED, Toast.LENGTH_SHORT).show()
}
} catch (e: GoogleIdTokenParsingException) {
Log.e(TAG, "Received an invalid google id token response", e)
}
} else {
// Catch any unrecognized custom credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}

else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}
}

fun signUpCredentialManager() {
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
request = request,
context = context,
)
handleSignIn(result)
} catch (e: GetCredentialException) {
showFilterByAuthorizedAccounts = false
Log.e(TAG, e.message.toString())
signUpCredentialManager()
showProgress = true
}
}
}

fun signUp() {
signUpCredentialManager()
showProgress = true
}

SignupMethodContentScreen(
showProgress = showProgress,
onSignup = {
signUp()
}
)
}

@Composable
fun SignupMethodContentScreen(
showProgress: Boolean,
onSignup: () -> Unit,
) {

Box(
modifier = Modifier,
) {
Column(
modifier = Modifier
.fillMaxSize()
.background(color = Color.White),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = Modifier.padding(top = 16.dp),
text = stringResource(id = R.string.feature_auth_create_an_account)
)
OutlinedButton(
modifier = Modifier.padding(top = 48.dp),
onClick = {
onSignup.invoke()
},
border = BorderStroke(1.dp, Color.LightGray),
shape = RoundedCornerShape(4.dp),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.Black)
) {
Text(
text = stringResource(id = R.string.sign_up).uppercase(),
style = MaterialTheme.typography.labelMedium
)
}
}
if (showProgress) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(top = 140.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(64.dp),
color = Color.Black,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
}
}
}
}

fun GoogleIdTokenCredential?.signUpWithForm(
context: Context,
signOutGoogleClient: () -> Unit
) {
val googleIdTokenCredential = this
val intent = Intent(context, SignUpFormActiivty::class.java)
if (googleIdTokenCredential != null) {
intent.putExtra(Constants.GOOGLE_PHOTO_URI, googleIdTokenCredential.profilePictureUri)
intent.putExtra(Constants.GOOGLE_DISPLAY_NAME, googleIdTokenCredential.displayName)
intent.putExtra(Constants.GOOGLE_EMAIL, googleIdTokenCredential.data.getString("com.google.android.libraries.identity.googleid.BUNDLE_KEY_ID"))
intent.putExtra(Constants.GOOGLE_FAMILY_NAME, googleIdTokenCredential.familyName)
intent.putExtra(Constants.GOOGLE_GIVEN_NAME, googleIdTokenCredential.givenName)
}
context.startActivity(intent)
signOutGoogleClient.invoke()
}

@Preview
@Composable
fun SignupMethodContentScreenPreview() {
MaterialTheme {
SignupMethodContentScreen(true, {}, {})
}
}

finally, we have implemented with Jetpack compose example.

signUpWithForm a function is the implementation of a custom form if you have it otherwise you can replace it with your ways of saving information or sending ID to the server to register etc. It’s up to your design flow.

Conclusion: You first need to build compose UI and on click of or visible of composeable function action call sign that will start the credentails manager google sign in flow. Once you got the response then it’s up to your flow you can send the info to server or move to custom form flow.

To support me for my upcoming blog, Please buy me a coffee

--

--

Rajan Maurya
AndroidByteSensei

Senior Software Engineer at OpenLane Inc, Open source contributor at Mifos Initiative and mentoring GSoC Students. https://github.com/therajanmaurya