Firebase登录遇RecaptchaAction错误:登录成功却报错无法跳转首页
问题详情
注册功能正常,登录时实际已成功完成身份认证,但同时会抛出错误。已尝试关闭Firebase的“邮箱枚举保护”,问题仍未解决。期望登录成功后跳转至HomeView,但报错持续出现。
错误日志
wa@gmail.com1212
I/FirebaseAuth(22477): Logging in as wa@gmail.com with empty reCAPTCHA token
W/System (22477): Ignoring header X-Firebase-Locale because its value was null.
D/Linux (22477): [Posix_connect Debug]Process com.ggsolutions.god_given :443
E/RecaptchaCallWrapper(22477): Initial task failed for action RecaptchaAction(action=signInWithPassword)with exception - The supplied auth credential is incorrect, malformed or has expired.
D/Linux (22477): [Posix_connect Debug]Process com.ggsolutions.god_given :443
V/NativeCrypto(22477): SSL shutdown failed: ssl=0xb400007ccbc49b98: I/O error during system call, Broken pipe
D/Linux (22477): [Posix_connect Debug]Process com.ggsolutions.god_given :443 .loginController:
登录控制器代码
class LoginController extends GetxController { //TODO: Implement LoginController final FirebaseAuth _auth = FirebaseAuth.instance; Rx<User?> user = Rx<User?>(null); Rx<UserData?> userData = Rx<UserData?>(null); RxBool isLoading = false.obs; @override void onInit() { super.onInit(); user.bindStream(_auth.authStateChanges()); checkAuthentification(); } void checkAuthentification() { print("I called"); _auth.authStateChanges().listen((user) async { if (user != null) { print("Inned also called"); await fetchUserData(user.uid); Get.off(HomeView(userData: userData.value!)); } }); } Future<void> signIn({ required String email, required String password, }) async { try { isLoading(true); print(email + password); await _auth .signInWithEmailAndPassword(email: email, password: password) .then((value) async { checkAuthentification(); }); } on FirebaseAuthException catch (e) { isLoading(false); Get.snackbar('Error hai', e.toString(), snackPosition: SnackPosition.BOTTOM); } } Future<void> fetchUserData(String userId) async { try { DocumentSnapshot userDataSnapshot = await FirebaseFirestore.instance .collection('users') .doc(userId) .get(); if (userDataSnapshot.exists) { userData.value = UserData.fromJson(userDataSnapshot.data() as Map<String, dynamic>); } else { Get.snackbar( "Error", "User Data not found for user- with ID : $userId"); } } catch (e) { print('Error fetching user data:$e'); } } }
解决方案
1. 修复重复监听authStateChanges的冲突
当前代码在onInit和signIn中重复调用checkAuthentification(),导致多次监听authStateChanges流,引发逻辑冲突。登录成功后无需手动触发监听,因为onInit中已绑定流监听。
修改signIn方法:
Future<void> signIn({ required String email, required String password, }) async { try { isLoading(true); print(email + password); // 直接等待登录完成,无需手动调用checkAuthentification await _auth.signInWithEmailAndPassword(email: email, password: password); } on FirebaseAuthException catch (e) { Get.snackbar('Error hai', e.toString(), snackPosition: SnackPosition.BOTTOM); } catch (e) { print('Unexpected login error: $e'); Get.snackbar('Error', 'An unexpected error occurred'); } finally { // 无论成功失败,都结束加载状态 isLoading(false); } }
2. 解决reCAPTCHA空token问题
日志中empty reCAPTCHA token是核心错误点,按以下步骤排查:
- 确认Android/iOS项目已正确配置Firebase安全验证(Android用SafetyNet,iOS用App Attest)。
- 检查
google-services.json(Android)或GoogleService-Info.plist(iOS)是否为Firebase控制台下载的最新版本,确保包含reCAPTCHA相关配置。 - 模拟器可能无法完成reCAPTCHA验证,建议使用真实设备测试。
3. 优化跳转逻辑避免空指针
fetchUserData是异步操作,直接使用userData.value!可能因数据未加载完成引发空指针异常,增加判空逻辑:
void checkAuthentification() { print("I called"); _auth.authStateChanges().listen((user) async { if (user != null) { print("Inned also called"); await fetchUserData(user.uid); // 判空后再执行跳转 if (userData.value != null) { Get.off(HomeView(userData: userData.value!)); } else { Get.snackbar("Error", "Failed to load user data"); } } }); }
内容的提问来源于stack exchange,提问作者Waleed Ahmed

