Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use CredentialManager for Google Sign-In #311

Merged
merged 3 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ dependencies {
implementation(libs.firebase.crashlytics.ktx)
implementation(libs.firebase.messaging)

implementation(libs.play.services.auth)
// Credentials
implementation(libs.androidx.credentials)
implementation(libs.androidx.credentials.playServicesAuth)
implementation(libs.googleid)

implementation(libs.koin.androidx.compose) {
exclude(group = "androidx.appcompat", module = "appcompat")
Expand Down
148 changes: 69 additions & 79 deletions androidApp/src/main/java/fr/paug/androidmakers/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package fr.paug.androidmakers

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.util.Log
Expand All @@ -15,28 +14,34 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.core.view.WindowCompat
import androidx.credentials.ClearCredentialStateRequest
import androidx.credentials.CredentialManager
import androidx.credentials.CustomCredential
import androidx.credentials.GetCredentialRequest
import androidx.credentials.exceptions.GetCredentialException
import androidx.lifecycle.lifecycleScope
import com.androidmakers.ui.MainLayout
import com.androidmakers.ui.common.SigninCallbacks
import com.androidmakers.ui.common.navigation.UserData
import com.androidmakers.ui.theme.AndroidMakersTheme
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.OnCompleteListener
import com.google.android.gms.tasks.Task
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 com.google.firebase.messaging.FirebaseMessaging
import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.auth.GoogleAuthProvider
import dev.gitlive.firebase.auth.auth
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
import org.koin.compose.KoinContext

class MainActivity : ComponentActivity() {

private val credentialManager: CredentialManager by lazy(LazyThreadSafetyMode.NONE) {
CredentialManager.create(this)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand Down Expand Up @@ -79,8 +84,8 @@ class MainActivity : ComponentActivity() {
versionCode = BuildConfig.VERSION_CODE.toString(),
deeplink = deeplink,
signinCallbacks = SigninCallbacks(
signin = { signin() },
signout = { signout() },
signin = ::signIn,
signout = ::signOut,
)
)
}
Expand All @@ -89,89 +94,74 @@ class MainActivity : ComponentActivity() {
}

private fun logFCMToken() {
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w("MainActivity", "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
}
val token = task.result
Log.d("MainActivity", token)
})
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

when (requestCode) {
REQ_SIGNIN -> {
val task: Task<GoogleSignInAccount> =
GoogleSignIn.getSignedInAccountFromIntent(data)
try {
val account: GoogleSignInAccount = task.getResult(ApiException::class.java)
val idToken = account.idToken
when {
idToken != null -> {
// Got an ID token from Google. Use it to authenticate
// with Firebase.
val firebaseCredential = GoogleAuthProvider.credential(idToken, null)
val auth = Firebase.auth

CoroutineScope(Dispatchers.Default).launch {
val result = auth.signInWithCredential(firebaseCredential)
// Sign in success, update UI with the signed-in user's information
lifecycleScope.launch {
UserData().apply {
userRepository.setUser(result.user)
val uid = result.user?.uid
if (uid != null) {
mergeBookmarksUseCase(uid)
}
}

println("user id=${result.user?.uid}")
println("idToken=${result.user?.getIdToken(true)}")
}
}
}

else -> {}
}
} catch (e: ApiException) {
e.printStackTrace()
lifecycleScope.launch {
UserData().userRepository.setUser(null)
}
lifecycleScope.launch {
try {
val token = FirebaseMessaging.getInstance().token.await()
Log.d(TAG, "FCM registration token: $token")
} catch (e: Exception) {
if (e is CancellationException) {
throw e
}
Log.w(TAG, "Fetching FCM registration token failed", e)
}
}
}

fun signout() {
val activity = this
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("985196411897-r7edbi9jgo3hfupekcmdrg66inonj0o5.apps.googleusercontent.com")
.build()
val googleSignInClient = GoogleSignIn.getClient(activity, gso)

private fun signOut() {
lifecycleScope.launch {
Firebase.auth.signOut()
googleSignInClient.signOut()
googleSignInClient.revokeAccess()
credentialManager.clearCredentialState(ClearCredentialStateRequest())
UserData().userRepository.setUser(null)
}
}

fun signin() {
val activity = this
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("985196411897-r7edbi9jgo3hfupekcmdrg66inonj0o5.apps.googleusercontent.com")
private fun signIn() {
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(SERVER_CLIENT_ID)
.build()
val googleSignInClient = GoogleSignIn.getClient(activity, gso)
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()

lifecycleScope.launch {
try {
val response = credentialManager.getCredential(this@MainActivity, request)
val credential = response.credential
if (credential is CustomCredential && credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
try {
handleGoogleIdTokenCredential(GoogleIdTokenCredential.createFrom(credential.data))
} catch (e: GoogleIdTokenParsingException) {
Log.e(TAG, "Received an invalid google id token response", e)
}
} else {
Log.e(TAG, "Unexpected type of credential")
}
} catch (e: GetCredentialException) {
Log.e(TAG, "Retrieving of credential failed", e)
UserData().userRepository.setUser(null)
}
}
}

private suspend fun handleGoogleIdTokenCredential(googleIdTokenCredential: GoogleIdTokenCredential) {
// Got an ID token from Google. Use it to authenticate with Firebase.GoogleSignIn
val firebaseCredential = GoogleAuthProvider.credential(googleIdTokenCredential.idToken, null)
val result = Firebase.auth.signInWithCredential(firebaseCredential)
// Sign in success, update UI with the signed-in user's information
with(UserData()) {
userRepository.setUser(result.user)
result.user?.uid?.let {
mergeBookmarksUseCase(it)
}
}

activity.startActivityForResult(googleSignInClient.signInIntent, REQ_SIGNIN)
Log.d(TAG, "user id=${result.user?.uid}")
Log.d(TAG, "idToken=${result.user?.getIdToken(true)}")
}

companion object {
const val REQ_SIGNIN = 33
private const val TAG = "MainActivity"
private const val SERVER_CLIENT_ID = "985196411897-r7edbi9jgo3hfupekcmdrg66inonj0o5.apps.googleusercontent.com"
}
}
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ sdk-compile = "34"
sdk-min = "24"
androidxActivity = "1.9.0"
androidxCore = "1.13.0"
androidxCredentials = "1.2.2"
androidxLifecycle = "2.7.0"
apollo = "4.0.0-beta.6"
compose = "1.6.4"
Expand All @@ -15,6 +16,7 @@ kotlin = "2.0.0-RC1"
coroutines = "1.8.0"
material3 = "1.2.1"
google-services-plugin = "4.4.1"
googleid = "1.1.0"
crashlytics-plugin = "2.9.9"
moko = "0.24.0-alpha-5"
moko-graphics = "0.9.0"
Expand All @@ -32,6 +34,8 @@ okio = "3.9.0"
[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" }
androidx-core = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" }
androidx-credentials = { group = "androidx.credentials", name = "credentials", version.ref = "androidxCredentials" }
androidx-credentials-playServicesAuth = { group = "androidx.credentials", name = "credentials-play-services-auth", version.ref = "androidxCredentials" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" }
apollo-adapters = { module = "com.apollographql.apollo3:apollo-adapters", version.ref = "apollo" }
Expand All @@ -48,6 +52,7 @@ espresso-core = "androidx.test.espresso:espresso-core:3.5.1"
firebase-bom = "com.google.firebase:firebase-bom:32.8.1"
firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" }
firebase-messaging = { module = "com.google.firebase:firebase-messaging" }
googleid = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "googleid" }
junit = "junit:junit:4.13.2"
kotlin-plugin-compose = { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
Expand Down
Loading