Firebase手机认证:onCodeSent()触发但Android设备未收到OTP
Firebase手机号认证:onCodeSent触发但收不到OTP短信
问题描述
在Android应用中基于Firebase实现手机号认证,验证流程可正常启动,UserRepositoryImpl类的onCodeSent()回调已触发,但设备始终无法收到包含OTP的短信。
已排查内容
- 使用的手机号属于Firebase控制台「测试用手机号」列表
- 号码格式完全匹配控制台显示(包含国家码和“+”前缀)
- 设备可正常接收其他非认证类短信
相关代码片段
PhoneSignUpViewModel.kt
fun onSignUpClick(activity: Activity, phoneNumber: String) { viewModelScope.launch { isLoading = true // LOG: Check the number before sending to the repository Log.d("PhoneSignUpViewModel", "Phone number being sent to signUp: $phoneNumber") userRepository.signUp(phoneNumber) // No need to pass token here .collect { result -> when (result) { is Result.Success -> { // Update the shared state holder (verificationId and token are in result.data) otpSharedStateHolder.otpVerificationData.value = OtpVerificationData( result.data.verificationId ?: "", result.data.token ) isLoading = false _events.emit(Event.NavigateToOtpVerification) } is Result.Error -> { showError = true isLoading = false } is Result.Loading -> { isLoading = true } } } } }
UserRepositoryImpl.kt
override fun signUp( phoneNumber: String, token: PhoneAuthProvider.ForceResendingToken? ): Flow<Result<User>> = callbackFlow { trySend(Result.Loading) launch { try { // LOG: Verify the number format received by the repository Log.d("UserRepositoryImpl", "Phone number received in signUp: $phoneNumber") val optionsBuilder = PhoneAuthOptions.newBuilder(firebaseAuth) .setPhoneNumber(phoneNumber) .setTimeout(60L, TimeUnit.SECONDS) .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() { override fun onVerificationCompleted(credential: PhoneAuthCredential) { // (Unlikely to happen for phone auth) launch { try { firebaseAuth.signInWithCredential(credential).await() val user = firebaseAuth.currentUser trySend( Result.Success( User( phoneNumber = phoneNumber, id = user?.uid ) ) ) } catch (e: Exception) { trySend( Result.Error( Exception("Sign-in failed: ${e.message}") ) ) } } } override fun onVerificationFailed(e: FirebaseException) { Log.e( "UserRepositoryImpl", "onVerificationFailed: ${e.message}", e ) trySend(Result.Error(e)) } override fun onCodeSent( verificationId: String, forceResendingToken: PhoneAuthProvider.ForceResendingToken ) { Log.d("UserRepositoryImpl", "onCodeSent: $verificationId") trySend( Result.Success( User( phoneNumber = phoneNumber, verificationId = verificationId, token = forceResendingToken ) ) ) close() // Close the callbackFlow } }) if (token != null) { optionsBuilder.setForceResendingToken(token) } val options = optionsBuilder.build() PhoneAuthProvider.verifyPhoneNumber(options) } catch (e: Exception) { trySend(Result.Error(e)) } } awaitClose { /* Clean up resources if needed */ } }.catch { emit(Result.Error(it as Exception)) }
Logcat输出
- D/PhoneSignUpViewModel: Phone number being sent to signUp: +16505550100
- D/UserRepositoryImpl: Phone number received in signUp: +16505550100
- D/UserRepositoryImpl: onCodeSent: [VERIFICATION_ID]
已尝试操作
- 遵循Firebase手机号认证文档配置(对Play Integrity和App Check存在疑惑)
- 在Firebase控制台添加了调试令牌
技术栈
- Kotlin
- Jetpack Compose
- Hilt
- Firebase Authentication
- Play Integrity API(已配置但理解不深)
解决方案
1. 直接使用测试号码的预设OTP
Firebase控制台添加的测试手机号无需接收实际短信,每个测试号码对应固定的预设验证码,可直接在控制台「测试用手机号」列表中查看该值,输入即可完成验证。
2. 排查Play Integrity/App Check配置
- 若开启了App Check,确认调试设备的调试令牌已正确添加到Firebase控制台,且App Check规则未拦截认证请求;可暂时关闭App Check(仅调试阶段)测试,若恢复正常,再重新配置Play Integrity验证规则。
- 检查Play Integrity的配置状态,确保应用签名、包名与控制台配置完全匹配,避免因验证失败导致短信发送被阻断。
3. 检查短信模板合规性
确认Firebase认证控制台的短信模板符合运营商规范,调试阶段可直接使用Firebase默认模板测试,避免自定义模板因内容违规被拦截。
4. 设备环境排查
- 确保设备网络可正常访问Firebase服务,部分地区运营商可能拦截国际短信(如+1开头的测试号),可尝试切换网络环境(如使用VPN)或添加本地测试号码验证。
- 检查设备是否开启了短信过滤功能,排除系统或第三方应用拦截认证短信的可能。
内容的提问来源于stack exchange,提问作者Aashish Ace
相关产品推荐
相关产品推荐

