使用Firebase谷歌登录时出现invalid-credential错误的原因排查
Firebase谷歌登录二次登录失败:invalid-credential 错误分析与修复
问题现象
首次通过谷歌账号登录Firebase可正常完成,但退出账号后再次尝试登录时失败,触发以下错误:
Error signing in: [firebase_auth/invalid-credential] The supplied auth credential is incorrect, malformed or has expired. [ ID Token issued at 1733745109 is stale to sign-in.
用户实现代码
class AuthService { final FirebaseAuth _firebaseAuth = FirebaseAuth.instance; final GoogleSignIn _googleSignIn = GoogleSignIn(); // GoogleSignIn instance // Sign in method with Google Sign-In and Firebase Authentication Future<User?> signIn() async { try { // Step 1: Trigger the Google Sign-In process final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); if (googleUser == null) { // If the user cancels the sign-in process print("Google Sign-In canceled."); return null; } // Step 2: Retrieve authentication details from Google final GoogleSignInAuthentication googleAuth = await googleUser.authentication; // Step 3: Create Firebase credential from Google authentication details final AuthCredential credential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); // Step 4: Sign in to Firebase with the Google credentials final UserCredential userCredential = await _firebaseAuth.signInWithCredential(credential); return userCredential.user; } catch (e) { print("Error signing in with Google: $e"); return null; } } // Get the current user from Firebase Future<User?> getCurrentUser() async { try { User? user = _firebaseAuth.currentUser; if (user != null) { await user.getIdToken(true); // Force token refresh if needed } return user; } catch (e) { print("Error getting current user: $e"); return null; } } // Check if the user is signed in with Firebase bool isSignedIn() { return _firebaseAuth.currentUser != null; } // Manually refresh the token before performing authenticated operations Future<String?> getFreshToken() async { try { User? user = _firebaseAuth.currentUser; if (user != null) { String? token = await user.getIdToken(true); // Forces refresh return token; } else { print('No user is signed in'); return null; } } catch (e) { print('Error refreshing token: $e'); return null; } } }
错误原因分析
- GoogleSignIn本地会话未清除:当前代码未实现完整的登出逻辑,退出时仅清除了Firebase的用户会话,但
GoogleSignIn实例仍在本地保留着之前的用户授权缓存。再次调用signIn()时,GoogleSignIn会直接返回缓存的旧会话信息,其中的idToken已经过期,导致Firebase验证失败。 - 首次登录正常的原因:首次登录时GoogleSignIn无本地缓存,会触发完整的账号选择、授权流程,获取的是新鲜有效的token,因此可以正常通过Firebase验证。
修复方案
1. 实现完整的登出方法
在AuthService中添加同时退出GoogleSignIn和Firebase的方法,确保本地缓存被彻底清除:
Future<void> signOut() async { await _googleSignIn.signOut(); // 先清除Google本地会话 await _firebaseAuth.signOut(); // 再清除Firebase会话 }
注意:必须先调用_googleSignIn.signOut(),再调用Firebase的signOut,避免GoogleSignIn残留旧会话。
2. 强制每次登录获取新token(可选)
如果需要用户每次登录都重新选择账号或获取新的授权,可以在signIn()方法开头先检查并清除GoogleSignIn的会话:
Future<User?> signIn() async { try { // 登录前先清除GoogleSignIn的旧会话 if (await _googleSignIn.isSignedIn()) { await _googleSignIn.signOut(); } final GoogleSignInAccount? googleUser = await _googleSignIn.signIn(); // 后续原有逻辑... } catch (e) { print("Error signing in with Google: $e"); return null; } }
3. 辅助排查:验证token有效期
可以在获取Google认证信息后,解码idToken查看过期时间,确认是否为过期问题:
final GoogleSignInAuthentication googleAuth = await googleUser.authentication; // 解码idToken的payload部分(需要dart:convert库) import 'dart:convert'; import 'dart:typed_data'; String? decodeIdToken(String token) { final parts = token.split('.'); if (parts.length != 3) return null; final payload = parts[1]; final normalized = base64Url.normalize(payload); final bytes = base64Url.decode(normalized); return utf8.decode(bytes); } // 使用示例 if (googleAuth.idToken != null) { print("Token Payload: ${decodeIdToken(googleAuth.idToken!)}"); }
解码后可查看exp字段(过期时间戳),确认是否早于当前时间。
内容的提问来源于stack exchange,提问作者Satya
相关产品推荐
相关产品推荐

