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

Jetpack Compose中使用CredentialManager实现Google登录报错

解决Jetpack Compose中Google登录的"NoCredentialException: Developer console is not set up correctly"问题

问题现象

在Jetpack Compose中实现Google登录时,调用credentialManager.getCredential始终抛出以下异常:

androidx.credentials.exceptions.NoCredentialException: During begin sign in, failure response from one tap: 10: Developer console is not set up correctly.

已尝试过重置SHA-1指纹、创建Web/Android类型的OAuth凭据、切换Web/Android客户端ID、重建密钥库(debug/release版)、更换测试设备、等待配置生效等操作,问题仍未解决。

错误堆栈

Process: com.metrofriedchicken.metrofc, PID: 20630
androidx.credentials.exceptions.NoCredentialException: During begin sign in, failure response from one tap: 10: Developer console is not set up correctly.
at androidx.credentials.playservices.controllers.CredentialProviderBaseController$Companion.getCredentialExceptionTypeToException$credentials_play_services_auth_release(CredentialProviderBaseController.kt:110)
at androidx.credentials.playservices.controllers.BeginSignIn.CredentialProviderBeginSignInController$resultReceiver$1$onReceiveResult$1.invoke(CredentialProviderBeginSignInController.kt:93)

相关代码

object DeveloperScreen{

    const val GOOGLE_WEB_CLIENT_ID = "xxxxxxxxxh5n94bxxxxxxxxxxx1.apps.googleusercontent.com"
//    const val GOOGLE_WEB_CLIENT_SECRET = "GxxxxxxxxCinwYDR8KQi3-wD9ugT311"
//    const val GOOGLE_ANDROID_CLIENT_ID = "3xxxxxxx80-7ja1n9nknxxxxxxxxxxxxxxxxxxxens33k.apps.googleusercontent.com"


    private suspend fun googleSignIn(context : Context) {


        val credentialManager = androidx.credentials.CredentialManager.create(context)

        val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
            .setFilterByAuthorizedAccounts(false)
            .setServerClientId(GOOGLE_WEB_CLIENT_ID)
            .build()

        Log.i("auth-google-googleIdOption", googleIdOption.toString())

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(googleIdOption)
            .build()

        Log.i("auth-google-credentialRequest", request.toString())


        val result = credentialManager.getCredential(
            request = request,
            context = context,
        )

        Log.i("auth-google-result", result.toString())

    }

    @Composable
    public fun DeveloperScreen1(navController : NavHostController, context : Context){

        var phoneNumber by rememberSaveable { mutableStateOf("") }
        var email by rememberSaveable { mutableStateOf("")}
        var otpCode by rememberSaveable { mutableStateOf("") }

        var useEmail by rememberSaveable { mutableStateOf(false)}

        var message by rememberSaveable { mutableStateOf("")}

        val coroutineScope = rememberCoroutineScope()

        Column {

            var user : User? = getCurrentUser()

            Text(
                textAlign = TextAlign.Center,
                fontSize = 5.em,
                text = "DEV SCREEN",
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 10.dp)

            )

            Text(
                text = message,
                color = Color.Black
            )

            Button(
                onClick = {
                    coroutineScope.launch {
                        googleSignIn(context)
                    }
                }
            ) {
                Text(text = "Google Sign In")
            }
        }
    }
}

var screenHeight = 0.dp
var screenWidth = 0.dp

var authViewModel = AuthViewModel()

class MainActivity : ComponentActivity() {

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

        Log.d("Initialize", "App Started")

        setContent {
            var context: Context = LocalContext.current

            Box(
                Modifier
                    .background(color = Color(0xFFBDC6CA))
                    .safeDrawingPadding()
            ) {
                MetroFCTheme {
                    MainContent(context)
                }
            }
        }

        actionBar?.hide()
        WindowCompat.setDecorFitsSystemWindows(window, false)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
        } else {
            window.insetsController?.apply {
//                hide(WindowInsetsAndroid.Type.statusBars())
                systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            }
        }
    }
}

@RequiresApi(Q)
@Composable
fun MainContent(
    context : Context
){

    var user by remember{ mutableStateOf<User?>(null) }

    LaunchedEffect(key1 = user) {
        refreshMenu()
    }

    LaunchedEffect(key1 = "login") {
        user = loginAnonymously()
        Log.d("DB-User", "USER LOGIN SUCCESSFUL : $user")
    }

    Log.d("DB", "2." + user.toString())
    Log.d("Initialize","Database Connected")

    val configuration = LocalConfiguration.current
    screenHeight = configuration.screenHeightDp.dp
    screenWidth = configuration.screenWidthDp.dp

    var onDashboard by remember {mutableStateOf(true)}
    var onMenu = false
    val updateStates = { dashboard: Boolean, menu: Boolean ->
        onDashboard = dashboard
        onMenu = menu
    }

    val navController = rememberNavController()

    Scaffold(
        bottomBar = {
            if(authViewModel.isLoggedIn) NavigationBar(navController = navController, onDashboard, onMenu, updateStates)
        },
        containerColor = MaterialTheme.colorScheme.background,
        contentWindowInsets = WindowInsets(0.dp),
        modifier = Modifier.fillMaxSize()
    ) { innerPadding ->
        val scrollState = rememberScrollState()
        val padding = innerPadding

        NavHost(
            navController = navController,
            startDestination = if (authViewModel.isLoggedIn) Route.DEV_SCREEN else Route.DEV_SCREEN,
            modifier = Modifier.fillMaxSize()
        ) {
            composable(route = Route.DEV_SCREEN){
                DeveloperScreen1(navController, context)
            }
            composable(route = Route.DASHBOARD_SCREEN) {
                authViewModel.login(user)
                DashboardScreen()
            }
            composable(route = Route.MENU_SCREEN) {
                authViewModel.login(user)
                MenuScreen(user)
            }
        }
    }
}

排查与解决方案

  1. 核对OAuth客户端ID配置

    • 确保Google Cloud中Android客户端ID的包名与应用包名com.metrofriedchicken.metrofc完全一致
    • 通过./gradlew signingReport获取debug和release版的SHA-1指纹,复制到对应客户端ID的配置中,注意不要混淆debug和release指纹
  2. 完成OAuth同意屏幕配置

    • 进入Google Cloud的OAuth同意屏幕页面,填写应用名称、支持邮箱,添加openid、email、profile等必要范围
    • 测试阶段将状态设为"内部测试",并添加测试用的Google账号;正式发布需将状态改为"发布"
  3. 修正GetGoogleIdOption配置

    • 如果仅在客户端验证身份,使用setClientId传入Android客户端ID,而非setServerClientId
    • 如果需要服务器端验证,setServerClientId必须使用Web应用的客户端ID,且该Web应用已在Google Cloud中正确配置
  4. 检查依赖版本
    确保项目中添加了最新的Credential Manager及Google登录依赖:

    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"
    
  5. 清除缓存

    • 清除应用的缓存和数据
    • 清除设备上Google Play服务的缓存,然后重启设备重试
  6. 验证测试账号
    确保测试设备登录的Google账号已添加到OAuth同意屏幕的测试用户列表中(若为内部测试状态)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:17:15