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) } } } }
排查与解决方案
核对OAuth客户端ID配置
- 确保Google Cloud中Android客户端ID的包名与应用包名
com.metrofriedchicken.metrofc完全一致 - 通过
./gradlew signingReport获取debug和release版的SHA-1指纹,复制到对应客户端ID的配置中,注意不要混淆debug和release指纹
- 确保Google Cloud中Android客户端ID的包名与应用包名
完成OAuth同意屏幕配置
- 进入Google Cloud的OAuth同意屏幕页面,填写应用名称、支持邮箱,添加
openid、email、profile等必要范围 - 测试阶段将状态设为"内部测试",并添加测试用的Google账号;正式发布需将状态改为"发布"
- 进入Google Cloud的OAuth同意屏幕页面,填写应用名称、支持邮箱,添加
修正
GetGoogleIdOption配置- 如果仅在客户端验证身份,使用
setClientId传入Android客户端ID,而非setServerClientId - 如果需要服务器端验证,
setServerClientId必须使用Web应用的客户端ID,且该Web应用已在Google Cloud中正确配置
- 如果仅在客户端验证身份,使用
检查依赖版本
确保项目中添加了最新的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"清除缓存
- 清除应用的缓存和数据
- 清除设备上Google Play服务的缓存,然后重启设备重试
验证测试账号
确保测试设备登录的Google账号已添加到OAuth同意屏幕的测试用户列表中(若为内部测试状态)
内容的提问来源于stack exchange,提问作者Derwin Oesmanto
相关产品推荐
相关产品推荐

