You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Credential Manager首次登录报“No credentials available”问题排查

新版Credential Manager API Google登录首次失败问题

问题现象

使用新版Credential Manager API实现Google登录时,应用安装后首次登录尝试始终失败,报错No credentials available,但多次尝试或等待一段时间后可正常完成登录。

相关实现代码

GetGoogleCredentialUseCase

class GetGoogleCredentialUseCase @Inject constructor(
    @ApplicationContext private val context: Context
) {
    suspend fun getGoogleCredential(): GetCredentialResponse = withContext(Dispatchers.IO) {
        Log.d("LoginDebug", "Starting Google login attempt")

        val auth = FirebaseAuth.getInstance()
        Log.d("LoginDebug", "Firebase login status before credential request: ${auth.currentUser != null}")

        val credentialManager = createCredentialManager()
        Log.d("LoginDebug", "createCredentialManager completed, ${credentialManager}")

        val request = createCredentialRequest()
        Log.d("LoginDebug", "createCredentialRequest completed, ${request}")

        fetchCredential(credentialManager, request)
    }

    private fun createCredentialManager(): CredentialManager {
        try {
            val manager = CredentialManager.create(context)
            Log.d("CredentialDebug", "CredentialManager creation successful")
            return manager
        } catch (e: Exception) {
            Log.e("CredentialDebug", "CredentialManager creation failed: ${e.javaClass.simpleName} - ${e.message}", e)
            throw e
        }
    }

    private fun createCredentialRequest(): GetCredentialRequest {
        val clientId = BuildConfig.GOOGLE_CLIENT_ID
        Log.d("CredentialDebug", "Using Google client ID: $clientId")

        try {
            // Check Google Play Services status
            val apiAvailability = GoogleApiAvailability.getInstance()
            val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
            Log.d("CredentialDebug", "Google Play Services status: $resultCode (0 is success)")

            // Check Google account status
            val accountManager = AccountManager.get(context)
            val accounts = accountManager.getAccountsByType("com.google")
            Log.d("CredentialDebug", "Number of Google accounts registered on device: ${accounts.size}")

            // Check security providers
            try {
                val installedProviders = Security.getProviders().map { it.name }
                Log.d("CredentialDebug", "Installed security providers: $installedProviders")
            } catch (e: Exception) {
                Log.w("CredentialDebug", "Failed to retrieve security providers list", e)
            }

            val googleIdOption = GetSignInWithGoogleOption.Builder(clientId)
                .build()
            Log.d("CredentialDebug", "GetSignInWithGoogleOption creation successful")

            val request = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()
            Log.d("CredentialDebug", "GetCredentialRequest creation successful")
            return request
        } catch (e: Exception) {
            Log.e("CredentialDebug", "Authentication request creation failed: ${e.javaClass.simpleName} - ${e.message}", e)
            throw e
        }
    }

    @SuppressLint("NewApi")
    private suspend fun fetchCredential(
        credentialManager: CredentialManager,
        request: GetCredentialRequest
    ): GetCredentialResponse {
        return try {
            Log.d("CredentialDebug", "getCredential call started, current thread: ${Thread.currentThread().name}")

            // Check Firebase authentication status
            val firebaseAuth = FirebaseAuth.getInstance()
            Log.d("CredentialDebug", "Firebase login status: ${firebaseAuth.currentUser != null}")

            // Check system time
            val currentTime = System.currentTimeMillis()
            val formattedTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(
                Date(currentTime)
            )
            Log.d("CredentialDebug", "Current system time: $formattedTime")

            withContext(Dispatchers.IO) {
                try {
                    credentialManager.getCredential(context, request)
                } catch (e: Exception) {
                    Log.e("CredentialDebug", "Authentication request failed in IO context: ${e.javaClass.simpleName}", e)
                    throw e
                }
            }
        } catch (e: Exception) {
            Log.e("CredentialDebug", "Authentication request failed: ${e.javaClass.simpleName} - ${e.message}", e)
            throw e
        }
    }
}

ViewModel登录逻辑

override fun signInWithGoogle() {
    viewModelScope.launch {
        authManager.startLoading()

        try {
            // 1. Check initialization
            appStateHolder.isFirebaseInitialized.first { it }

            // 2. Allow time for authentication service preparation
            delay(1500L)

            // 3. Retry logic with clearCredentials failure handling
            var attempt = 0
            var lastError: Exception? = null

            while (attempt < 3) {
                try {
                    if (attempt > 0) {
                        delay(1000L * attempt)
                        Log.d("LoginFlow", "Google login retry ${attempt+1}/3")
                    }

                    // Wrap clearCredentials with try-catch
                    try {
                        authManager.clearCredentials()
                    } catch (e: Exception) {
                        Log.w("LoginFlow", "Credential initialization failed, continuing: ${e.message}")
                    }

                    val result = authManager.getGoogleCredential()
                    processSignInResult(result)
                    break

                } catch (e: Exception) {
                    val errorMsg = e.message ?: "Unknown error"
                    Log.e("LoginFlow", "Attempt ${attempt+1} failed: $errorMsg", e)
                    lastError = e
                    attempt++

                    if (!shouldRetry(e) || attempt >= 3) {
                        throw lastError ?: RuntimeException("Authentication failed")
                    }
                }
            }

        } catch (e: Exception) {
            Log.e("LoginFlow", "signInWithGoogle error emergency!", e)
            handleException(e)
        } finally {
            authManager.stopLoading()
        }
    }
}

日志信息

失败尝试日志

2025-02-21 01:56:37.709  3811-3811  LoginDebug              com.example.myApp              D  Starting Google login attempt
2025-02-21 01:56:38.426  3811-4822  CredManProvService      com.example.myApp              I  GetCredentialResponse error returned from framework
2025-02-21 01:56:38.427  3811-3811  LoginDebug              com.example.myApp              D  No authorized accounts, retrying with all accounts
2025-02-21 01:56:38.714  3811-4822  CredManProvService      com.example.myApp              I  GetCredentialResponse error returned from framework
2025-02-21 01:56:38.714  3811-3811  LoginDebug              com.example.myApp              D  All GetGoogleIdOption attempts failed, trying GetSignInWithGoogleOption
2025-02-21 01:56:39.720  3811-4065  CredManProvService      com.example.myApp              I  GetCredentialResponse error returned from framework
2025-02-21 01:56:39.726  3811-3811  LoginFlow               com.example.myApp              E  Attempt 1 failed: No credentials available
                                                                                               b1.h: No credentials available

成功尝试日志

2025-02-21 01:52:11.656 12221-12300 EGL_emulation           com.example.myApp              D  app_time_stats: avg=396.17ms min=4.37ms max=1641.80ms count=6
2025-02-21 01:52:13.309 12221-12300 EGL_emulation           com.example.myApp              D  app_time_stats: avg=28.43ms min=1.67ms max=959.90ms count=41
2025-02-21 01:52:14.778 12221-12300 EGL_emulation           com.example.myApp              D  app_time_stats: avg=24.68ms min=2.26ms max=780.65ms count=41
2025-02-21 01:52:16.345 12221-12294 CredentialDebug         com.example.myApp              D  CredentialManager creation successful
2025-02-21 01:52:16.356 12221-12294 CredManProvService      com.example.myApp              I  In CredentialProviderFrameworkImpl onClearCredential
2025-02-21 01:52:17.371 12221-12294 FirebaseAuth            com.example.myApp              D  Notifying id token listeners about a sign-out event.
2025-02-21 01:52:17.372 12221-12294 FirebaseAuth            com.example.myApp              D  Notifying auth state listeners about a sign-out event.
2025-02-21 01:52:17.381 12221-12221 LoginDebug              com.example.myApp              D  Starting Google login attempt
2025-02-21 01:52:17.720 12221-12499 CredManProvService      com.example.myApp              I  GetCredentialResponse error returned from framework
2025-02-21 01:52:17.720 12221-12221 LoginDebug              com.example.myApp              D  No authorized accounts, retrying with all accounts
2025-02-21 01:52:17.795 12221-12499 CredManProvService      com.example.myApp              I  GetCredentialResponse error returned from framework
2025-02-21 01:52:17.795 12221-12221 LoginDebug              com.example.myApp              D  All GetGoogleIdOption attempts failed, trying GetSignInWithGoogleOption

关键观察

成功案例中,尝试GetSignInWithGoogleOption后无错误日志;失败案例中则出现明确的No credentials available错误。

已尝试方案

  • 使用不同凭证请求策略:GetGoogleIdOption+setFilterByAuthorizedAccounts(true)、GetGoogleIdOption+setFilterByAuthorizedAccounts(false)、GetSignInWithGoogleOption
  • 在尝试之间添加延迟
  • 尝试前清除凭证
  • 确认Google Play Services可用

疑问与解答

1. 为何多次尝试或等待后认证可成功?

首次安装后,Google Play Services相关的认证服务、账户同步或Credential Manager的底层缓存尚未完成初始化。这些后台操作需要时间执行,等待或重试时,系统已经完成了必要的准备工作,因此请求能够成功。

2. Credential Manager是否需要特定初始化操作才能正常工作?

Credential Manager本身通过CredentialManager.create(context)即可完成初始化,但它依赖的Google Play Services组件、系统账户服务需要时间完成启动和同步。首次安装后,这些依赖服务可能还在后台初始化,导致Credential Manager无法获取到可用凭证。

3. 这是否与Google Play Services后台初始化有关?

是的,完全相关。Google Play Services负责处理Google账户的认证、凭证管理等核心逻辑,首次安装应用后,Play Services可能需要完成账户同步、服务注册等异步操作,未完成时Credential Manager的请求就会失败。

4. 在此场景下处理“No credentials available”的最佳实践是什么?

  • 智能重试策略:针对该错误实现指数退避重试(比如第一次延迟1s,第二次2s,最多3次),避免固定延迟,减少不必要的等待。
  • 前置检查:在发起登录请求前,确保Google Play Services已完全可用(不仅是isGooglePlayServicesAvailable返回成功,还可监听GoogleApiAvailability的回调或等待账户同步完成)。
  • 用户友好提示:首次失败时告知用户“正在准备登录服务,请稍候”,而非直接提示登录失败,提升用户体验。
  • 避免频繁清除凭证:clearCredentials可能干扰Credential Manager的缓存机制,仅在确定有缓存问题时使用,不要每次重试都调用。

内容的提问来源于stack exchange,提问作者Yoon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:32:03