Home > Net >  Jetpack Compose Firebase Signin With Google, no error but not working, what am I missing
Jetpack Compose Firebase Signin With Google, no error but not working, what am I missing

Time:09-20

I am new to Android development and so far I have been really keen to use Alex Mamo's lecture to adapt my code and find best practices. I am trying to implement the one tap sign in button to my app. So far I was able to reproduce what Alex posted on his Github and works perfectly. But when it comes to my integration to my app, it's a different story. I get no errors and very little signs that it is almost working, after 2 days of trying I come to a point where I don't know what I am missing to make it work.

The 2 main differences from my stack to his are my AppModule.kt where I have added the logic to write on a different collection (that is my first thought, that could've blocked me)

object AppModule {
@Provides
fun provideContext(
    app: Application
): Context = app.applicationContext

@Provides
fun provideFirebaseAuth() = Firebase.auth

@Provides
fun provideFirebaseFirestore() = Firebase.firestore

@Provides
fun provideItemRef(
    db: FirebaseFirestore
) = db
    .collection(COLLECTION_NAME)

@Provides
fun provideItemRepository(
    itemsRef: CollectionReference
): BudgetRepository = BudgetRepositoryImpl(itemsRef)

@Provides
fun provideUseCases(
    repo: BudgetRepository
) =  UseCases(
    getItems = GetItems(repo)
)

@Provides
fun provideOneTapClient(
    context: Context
) = Identity.getSignInClient(context)

@Provides
@Named(SIGN_IN_REQUEST)
fun provideSignInRequest(
    app: Application
) = BeginSignInRequest.builder()
    .setGoogleIdTokenRequestOptions(
        BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
            .setSupported(true)
            .setServerClientId(app.getString(R.string.web_client_id))
            .setFilterByAuthorizedAccounts(true)
            .build())
    .setAutoSelectEnabled(true)
    .build()

@Provides
@Named(SIGN_UP_REQUEST)
fun provideSignUpRequest(
    app: Application
) = BeginSignInRequest.builder()
    .setGoogleIdTokenRequestOptions(
        BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
            .setSupported(true)
            .setServerClientId(app.getString(R.string.web_client_id))
            .setFilterByAuthorizedAccounts(false)
            .build())
    .build()

@Provides
fun provideGoogleSignInOptions(
    app: Application
) = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestIdToken(app.getString(R.string.web_client_id))
    .requestEmail()
    .build()

@Provides
fun provideGoogleSignInClient(
    app: Application,
    options: GoogleSignInOptions
) = GoogleSignIn.getClient(app, options)

@Provides
fun provideAuthRepository(
    auth: FirebaseAuth,
    oneTapClient: SignInClient,
    @Named(SIGN_IN_REQUEST)
    signInRequest: BeginSignInRequest,
    @Named(SIGN_UP_REQUEST)
    signUpRequest: BeginSignInRequest,
    signInClient: GoogleSignInClient,
    db: FirebaseFirestore
): AuthRepository = AuthRepositoryImpl(
    auth = auth,
    oneTapClient = oneTapClient,
    signInRequest = signInRequest,
    signUpRequest = signUpRequest,
    signInClient = signInClient,
    db = db
)

}

And the fact that I don't redirect to a profile page but to a dashboard. So this is it and the result is that sometimes I see the snackBar showing with progress bar but not going anywhere and sometimes not. Screen Capture of Emulator

setContent {
        MyApp() {
            navController = rememberAnimatedNavController()
            BudgetNavigation(
                navController = navController
            )
            checkAuthState()
        }
    }
}
private fun checkAuthState() {
    if(viewModel.isUserAuthenticated) {
        navigateToDashboardScreen()
    }
}

isUserAuthenticated is the one not receiving the TRUE value, if I place a ! in front I will navigate to my destination.

Please ask me anything if you need me to clarify, I would really like to get it as I have passed now 3 days on this issue...

Thank you :)

CodePudding user response:

Hope it will be helpful

internal class GoogleAuthActivityResultContract :
ActivityResultContract<Unit, Task<GoogleSignInAccount>>() {

override fun createIntent(context: Context, input: Unit): Intent {
    val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .requestIdToken(context.getString(R.string.default_web_client_id))
        .build()

    val client = GoogleSignIn.getClient(context, googleSignInOptions)
    return client.signInIntent
}

override fun parseResult(resultCode: Int, intent: Intent?): Task<GoogleSignInAccount> {
    return if (resultCode == Activity.RESULT_CANCELED) {
        Tasks.forCanceled()
    } else {
        GoogleSignIn.getSignedInAccountFromIntent(intent)
    }
}}

Important, that you have to put it on clickable

val googleAuthLauncher = rememberLauncherForActivityResult(
                GoogleAuthActivityResultContract(),
                welcomeViewModel::onGoogleAuthResult
            )

 Button(stringResource(id = R.string.login_option_google), modifier= Modifier.clickable{googleAuthLauncher.launch()})

and on your viewModel you can handle results as

  fun onGoogleAuthResult(task: Task<GoogleSignInAccount>) {
    if (task.isCanceled) return
    viewModelScope.launch {
        runCatching {
            val account = task.await()
            val authCredential = GoogleAuthProvider
                .getCredential(account.idToken, null)

            val authResult = Firebase.auth.signInWithCredential(authCredential).await()
            val authUser = requireNotNull(authResult.user) { "User from auth result is empty" }
            val tokenResult = authUser.getIdToken(true).await()
            val token = requireNotNull(tokenResult.token) { "Token from firebase is empty" }

            authRepository.loginByGoogleToken(token)
        }
            .onSuccess {
                Log.i("TAG", "Success")
            }
            .onFailureSafe {
                it.printStackTrace()
            }
    }
}

CodePudding user response:

I have found my issue and this is what solved it:

@Provides
fun provideBudgetRepository(
    db: FirebaseFirestore
): BudgetRepository = BudgetRepositoryImpl(
)

instead of:

@Provides
fun provideItemRef(
    db: FirebaseFirestore
) = db
    .collection(COLLECTION_NAME)

@Provides
fun provideItemRepository(
    itemsRef: CollectionReference
): BudgetRepository = BudgetRepositoryImpl(itemsRef)

@Provides
fun provideUseCases(
    repo: BudgetRepository
) =  UseCases(
    getItems = GetItems(repo)
)

it was the way I was reaching to Firebase through useCases in the MVVM architecture. To simplify I mimicked the way Firebase writes to Firestore once a User is logged in, thus removing the use of useCases in the architecture and using solely the viewModels to reach out to the repositories!

Thanks everyone for trying to help! This was motivating and helped me look further!

  • Related