Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,21 @@ internal suspend fun FirebaseAuthUI.signInWithGoogle(
autoSelectEnabled = provider.autoSelectEnabled
)
} catch (fallbackException: NoCredentialException) {
// Credential Manager doesn't distinguish "no account on device" from
// developer-side misconfiguration, so log the possible causes for
// debugging. Never surfaced to end users: the overwhelming majority
// hitting this genuinely have no account, and Firebase Console
// guidance would just confuse them.
Log.w(
"GoogleAuthProvider",
"No credential returned from Credential Manager after trying both " +
"authorized and all accounts. Possible causes: (1) no Google " +
"account on this device, (2) no Android OAuth client / SHA-1 " +
"registered for this app's package + signing certificate in the " +
"Firebase console, or (3) the Credential Manager Google ID " +
"provider is unavailable on this device.",
fallbackException
)
// No Google accounts available on device at all
throw AuthException.UnknownException(
message = "No Google accounts available.\n\nPlease add a Google account to your device and try again.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package com.firebase.ui.auth.configuration.auth_provider

import android.content.Context
import android.util.Log
import androidx.core.net.toUri
import androidx.credentials.CredentialManager
import androidx.credentials.exceptions.NoCredentialException
import androidx.test.core.app.ApplicationProvider
import com.firebase.ui.auth.AuthException
import com.firebase.ui.auth.AuthState
Expand Down Expand Up @@ -50,6 +52,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowLog

/**
* Comprehensive unit tests for Google Sign-In provider methods in FirebaseAuthUI.
Expand Down Expand Up @@ -437,6 +440,65 @@ class GoogleAuthProviderFirebaseAuthUITest {
assertThat(errorState.exception).isInstanceOf(AuthException.UnknownException::class.java)
}

@Test
fun `Sign in with Google when both credential attempts throw NoCredentialException logs diagnostic warning`() = runTest {
ShadowLog.clear()
val noCredentialException = NoCredentialException("No credential available")

`when`(
mockCredentialManagerProvider.getGoogleCredential(
context = eq(applicationContext),
credentialManager = any<CredentialManager>(),
serverClientId = eq("test-client-id"),
filterByAuthorizedAccounts = eq(true),
autoSelectEnabled = eq(false)
)
).thenAnswer { throw noCredentialException }

`when`(
mockCredentialManagerProvider.getGoogleCredential(
context = eq(applicationContext),
credentialManager = any<CredentialManager>(),
serverClientId = eq("test-client-id"),
filterByAuthorizedAccounts = eq(false),
autoSelectEnabled = eq(false)
)
).thenAnswer { throw noCredentialException }

val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
val googleProvider = AuthProvider.Google(
serverClientId = "test-client-id",
scopes = emptyList()
)
val config = authUIConfiguration {
context = applicationContext
providers {
provider(googleProvider)
}
}

try {
instance.signInWithGoogle(
context = applicationContext,
config = config,
provider = googleProvider,
authorizationProvider = mockAuthorizationProvider,
credentialManagerProvider = mockCredentialManagerProvider
)
throw AssertionError("Expected exception to be thrown")
} catch (e: AuthException) {
// User-facing message stays generic - never mentions Firebase Console/SHA-1
assertThat(e).isInstanceOf(AuthException.UnknownException::class.java)
assertThat(e.message).contains("No Google accounts available")
}

// Diagnostic detail goes to Logcat only, for developers
val diagnosticLog = ShadowLog.getLogs().firstOrNull {
it.type == Log.WARN && it.tag == "GoogleAuthProvider" && it.msg.contains("SHA-1")
}
assertThat(diagnosticLog).isNotNull()
}

@Test
fun `Sign in with Google when Firebase sign-in fails should throw AuthException`() = runTest {
val mockCredential = mock(AuthCredential::class.java)
Expand Down