Solusi GoogleSignInClient dan SignInClient Deprecated pada Firebase Auth dengan Google

Ahmad Arif Faizin
6 min readApr 24, 2024

--

Lohaloo, sampai jumpa lagi tutorial deprecated, kalau dilihat isinya medium ini memang banyaknya lebih ke mengatasi kode kode yang deprecated. Jadi, keterusan kalau nemu deprecated, hawanya ingin mendokumentasikan supaya yang lainnya juga tahu solusinya.

SignInClient

Kalau kamu ingin tahu penerapan Firebase Auth dengan metode yang lama (SignInClinet), kurang lebih kodenya seperti ini.

class LoginActivity : AppCompatActivity() {
...
private lateinit var googleSignInClient: GoogleSignInClient
private lateinit var auth: FirebaseAuth

override fun onCreate(savedInstanceState: Bundle?) {
....
// Configure Google Sign In
val gso = GoogleSignInOptions
.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()

googleSignInClient = GoogleSignIn.getClient(this, gso)

// Initialize Firebase Auth
auth = Firebase.auth

binding.signInButton.setOnClickListener {
signIn()
}
}

private fun signIn() {
val signInIntent = googleSignInClient.signInIntent
resultLauncher.launch(signInIntent)
}

private var resultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(result.data)
try {
// Google Sign In was successful, authenticate with Firebase
val account: GoogleSignInAccount = task.getResult(ApiException::class.java)!!
Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
firebaseAuthWithGoogle(account.idToken!!)
} catch (e: ApiException) {
// Google Sign In failed, update UI appropriately
Log.w(TAG, "Google sign in failed", e)
}
}
}

private fun firebaseAuthWithGoogle(idToken: String) {
val credential: AuthCredential = GoogleAuthProvider.getCredential(idToken, null)
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
val user: FirebaseUser? = auth.currentUser
updateUI(user)
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.exception)
updateUI(null)
}
}
}
...
}

Jadi, di sini kita menggunakan GoogleSignInClient dengan GoogleSignInOptions yang nantinya dapat digunakan untuk membuat Intent yang menghasilkan idToken dari GoogleSignInAccount. idToken inilah nanti yang digunakan oleh Firebase Auth sebagai credential.

Berikut hasil jadi UI-nya

Sayangnya, baru dapat laporan kalau ternyata Google SignInClient sudah deprecated.

Karena penasaran.. jadilah mengulik case ini selama setengah hari ini. Agak lucu sebenarnya karena setelah coba diperbaiki ternyata masih ada yang janggal. Penasaran seperti apa ceritanya?

checkitout~

BeginSignInClient

Setelah dilihat pada dokumentasinya, ternyata memang ada perubahan pada dokumentasinya Firebase Auth

Anda juga dapat melihatnya pada tutorial implementasi Firebase Assistant di dalam Android Studio.

Jika dilihat, dari penerapan di dalam fungsi firebaseAuthWithGoogle masih sama, tetapi ada beberapa perbedaan berikut.

  • Untuk web client id, yang sebelumnya disematkan di GoogleSignInOptions diubah menjadi BeginSignInRequest
  • GoogleSignInClient diubah menjadi general SignInClient (walaupun tidak terlihat di dokumentasi -_-, tapi bisa dicari di sample GitHub)
  • Yang bikin pusing adalah pada dokumentasi tidak disebutkan cara membuat StartActivityForResult, hanya onActivityResult, yang mana ini juga cara yang lama, yang terbaru seharusnya cukup menggunakan registerForActivityResult dengan ActivityResultLauncher

Setelah mengulik sana-sini melihat contekan di GitHub. Berikut ini adalah kode terbaru untuk menerapkan Google Sign In

class LoginActivity : AppCompatActivity() {
...
private lateinit var signInRequest: BeginSignInRequest
private lateinit var oneTapClient: SignInClient
private lateinit var auth: FirebaseAuth

override fun onCreate(savedInstanceState: Bundle?) {
...
// Configure Google Sign In
signInRequest = BeginSignInRequest.builder()
.setGoogleIdTokenRequestOptions(
BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
.setSupported(true)
// Your server's client ID, not your Android client ID.
.setServerClientId(getString(R.string.your_web_client_id))
// Only show accounts previously used to sign in.
.setFilterByAuthorizedAccounts(true)
.build()
)
.build()

oneTapClient = Identity.getSignInClient(this)
...
}

private fun signIn() {
oneTapClient.beginSignIn(signInRequest).addOnSuccessListener { result ->
try {
resultLauncher.launch(
IntentSenderRequest.Builder(result.pendingIntent.intentSender).build()
)
} catch (e: IntentSender.SendIntentException) {
Log.e("Error", "Couldn't start One Tap UI: ${e.localizedMessage}")
}
}.addOnFailureListener {
Log.d("Error", it.localizedMessage)
}
}

private var resultLauncher: ActivityResultLauncher<IntentSenderRequest> =
registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val googleCredential = oneTapClient.getSignInCredentialFromIntent(result.data)
val idToken = googleCredential.googleIdToken

if (idToken != null) {
firebaseAuthWithGoogle(idToken)
} else {
Log.d(TAG, "No ID token!")
}
}
}

Jika dilihat lagi, perbedaan dengan kode sebelumnya adalah untuk Intent bukan menggunakan StartActivityForResult, tetapi StartIntentSenderForResult.

Selain itu, pada ActivityResultLauncher, yang di wrap bukan Intent tetapi IntentSenderRequest, yang mana harus dibuat dulu dari intentSender yang didapat dari fungsi beginSignIn.

Sampai sini, kode bisa jalan dengan tampilan yang berbeda, yakni One Tap UI.

Namun, ternyata ehh ternyata, kode yang dari dokumentasi ini juga sudah deprecated. -_-

Di sana disebutkan bahwa pengganti yang direkomendasikan adalah Credential Manager. Makanan apalagi tuh? Yuk kita ulik di bagian selanjutnya!

Credential Manager

Credential Manager adalah Jetpack API terbaru yang support multiple login, baik pakai username-password, Sign In Google dan teman-temannya, dan juga passkey. Passkey ini termasuk teknologi terbaru yang hanya bisa dijalanin di Android 9 ke atas.

Goal utama dari Credential Manager adalah untuk menyamakan UI untuk seluruh mekanisme, jadi mau pakai cara apapun, UI-nya sama, beda dengan pendekatan sebelumnya yang UI-nya berbeda antar vendor.

Untuk pakai Credential Manager, kita perlu ubah dependency yang sebelumnya dari GMS jadi Credentials dan Identity.

//before
implementation(“com.google.android.gms:play-services-auth:21.1.0”)

//after
implementation("androidx.credentials:credentials:1.2.2")
implementation("androidx.credentials:credentials-play-services-auth:1.2.2")
implementation("com.google.android.libraries.identity.googleid:googleid:1.1.0")

Untuk dokumentasi dari Credential Manager sendiri cukup jelas, jadi bisa langsung kita ikuti saja tutorial di sini

Alhasil, beginilah hasil jadi kode berdasarkan dokumentasi tersebut.

class LoginActivity : AppCompatActivity() {
...
private fun signIn() {
val credentialManager = CredentialManager.create(this) //import from androidx.CredentialManager

val googleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(getString(R.string.your_web_client_id)) //from https://console.firebase.google.com/project/my-firebase-chat-2aac3/authentication/providers
.build()

val request = GetCredentialRequest.Builder() //import from androidx.CredentialManager
.addCredentialOption(googleIdOption)
.build()

lifecycleScope.launch {
try {
val result: GetCredentialResponse = credentialManager.getCredential( //import from androidx.CredentialManager
request = request,
context = this@LoginActivity,
)
handleSignIn(result)
} catch (e: GetCredentialException) { //import from androidx.CredentialManager
Log.d("Error", e.message.toString())
}
}
}

private 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 {
// Use googleIdTokenCredential and extract id to validate and authenticate on your server.
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
firebaseAuthWithGoogle(googleIdTokenCredential.idToken)
} 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")
}
}
}
...
}

Berikut beberapa hal yang membedakan dengan kode sebelumnya.

  • Untuk web client id, yang sebelumnya disematkan di BeginSignInRequest diubah menjadi GetGoogleIdOption.
  • Tidak ada lagi kode SignInClient dan Intent untuk menampilkan pilihan login, tetapi di sini kita menggunakan fungsi credentialManager.getCredential yang merupakan suspend function. Itulah mengapa di sini pakai lifecycleScope.launch.
  • Hasil dari GetCredentialResponse bisa bermacam-macam, karena seperti yang disebutkan sebelumnya bisa berupa username-password, passkey, dll. Untuk Sign In by Google kita menggunakan CustomCredential dengan tipe GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.

Okay, setelah dijalankan, tampilan Credential Manager akan terlihat seperti ini.

Sekian, informasi hari ini, semoga bermanfaat yah untuk Anda yang mengalami.

--

--