Migrate to google credential to authenticate

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-04 12:15:20 +02:00
parent b21ecd5449
commit 51ffc9a303
3 changed files with 54 additions and 80 deletions

View file

@ -123,6 +123,11 @@ dependencies {
// Splash Screen support prior to Android 12
implementation("androidx.core:core-splashscreen:1.0.1")
// Google Auth
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")
// Google service
implementation("com.google.android.gms:play-services-auth:21.2.0")
implementation(

View file

@ -51,6 +51,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialProviderConfigurationException
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalActivity
import com.pixelized.rplexicon.LocalSnack
@ -85,10 +87,9 @@ fun AuthenticationScreen(
versionVM: VersionViewModel = hiltViewModel(),
onSignIn: CoroutineScope.() -> Unit,
) {
val snack = LocalSnack.current
val context = LocalContext.current
val snack = LocalSnack.current
val activity = LocalActivity.current
val state = authenticationVM.rememberAuthenticationState()
Surface {
PartyBackground()
@ -105,17 +106,22 @@ fun AuthenticationScreen(
)
HandleAuthenticationState(
state = state,
state = authenticationVM.authenticationState,
onProgress = {
Dialog(onDismissRequest = { }) {
CircularProgressIndicator()
}
},
onSignIn = onSignIn,
onSignInError = {
snack.showSnackbar(
message = it?.message ?: context.getString(R.string.error__generic)
)
onSignInError = { exception ->
when (exception) {
// Ignore user cancellation.
is GetCredentialCancellationException -> Unit
else -> snack.showSnackbar(
message = exception?.message ?: context.getString(R.string.error__generic)
)
}
}
)
}

View file

@ -1,96 +1,59 @@
package com.pixelized.rplexicon.ui.screens.authentication
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.IntentSender.SendIntentException
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.credentials.Credential
import androidx.credentials.CredentialManager
import androidx.lifecycle.AndroidViewModel
import com.google.android.gms.auth.api.identity.GetSignInIntentRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.auth.api.identity.SignInCredential
import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import androidx.credentials.GetCredentialRequest
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
import com.pixelized.rplexicon.R
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
/**
* https://developer.android.com/identity/sign-in/credential-manager-siwg
*/
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
application: Application,
) : AndroidViewModel(application) {
class AuthenticationViewModel @Inject constructor() : ViewModel() {
private val context: Context get() = getApplication()
private var launcher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>? = null
private val state = mutableStateOf<AuthenticationStateUio>(
val authenticationState = mutableStateOf<AuthenticationStateUio>(
AuthenticationStateUio.Initial
)
@Composable
fun rememberAuthenticationState(): State<AuthenticationStateUio> {
launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult(),
onResult = {
if (it.resultCode == Activity.RESULT_OK) {
state.value = AuthenticationStateUio.Progress
// sign in request succeed. retrieve google credential
val googleCredential: SignInCredential = Identity
.getSignInClient(context)
.getSignInCredentialFromIntent(it.data)
// build firebase credential
val firebaseCredential = GoogleAuthProvider
.getCredential(googleCredential.googleIdToken, null)
// sign in to Firebase
Firebase.auth
.signInWithCredential(firebaseCredential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
state.value = AuthenticationStateUio.Success
} else {
state.value = AuthenticationStateUio.Failure(task.exception)
}
}
} else {
state.value = AuthenticationStateUio.Initial
}
},
)
return state
}
fun signIn(activity: Activity) {
state.value = AuthenticationStateUio.Initial
val credentialManager = CredentialManager.create(context = activity)
// build a request to sign in with google credential.
// At that point we do only use google sign in service
val request: GetSignInIntentRequest = GetSignInIntentRequest.builder()
.setServerClientId(context.getString(R.string.google_sign_in_id))
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
.Builder(serverClientId = activity.getString(R.string.google_sign_in_id))
.build()
// use the pre register launcher to start the sign in request intent.
Identity.getSignInClient(activity).getSignInIntent(request)
.addOnSuccessListener { result ->
try {
launcher?.launch(
IntentSenderRequest.Builder(result.intentSender).build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(credentialOption = signInWithGoogleOption)
.build()
viewModelScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
authenticationState.value = AuthenticationStateUio.Initial
}
try {
credentialManager.getCredential(
request = request,
context = activity,
)
withContext(Dispatchers.Main) {
authenticationState.value = AuthenticationStateUio.Success
}
} catch (exception: Exception) {
withContext(Dispatchers.Main) {
authenticationState.value = AuthenticationStateUio.Failure(
exception = exception,
)
} catch (exception: SendIntentException) {
state.value = AuthenticationStateUio.Failure(exception = exception)
}
}
.addOnFailureListener { exception ->
state.value = AuthenticationStateUio.Failure(exception = exception)
}
}
}
}