androidkotlinfirebase-authenticationclean-architecture

Error 'App verification failed - a valid Activity is required to complete the Recaptcha flow' in Firebase Phone Authentication on Kotlin


I'm trying to implement Firebase Phone Authentication using Clean Architecture in Kotlin, but I'm encountering an issue because I haven't added an Activity to the PhoneAuthOptions builder. As a result, I'm getting the error: 'App verification failed - a valid Activity is required to complete the Recaptcha flow.

class AuthenticationDataSourceImpl @Inject constructor(private val auth: FirebaseAuth) :
    AuthenticationDataSource {

    override suspend fun sendVerificationCode(phoneNumber: String): AuthFirebaseResult =
        suspendCancellableCoroutine { continuation ->
            val options = PhoneAuthOptions.newBuilder(auth)
                .setPhoneNumber(phoneNumber)
                .setTimeout(60L, java.util.concurrent.TimeUnit.SECONDS)
                .setCallbacks(object : OnVerificationStateChangedCallbacks() {
                    override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                        Log.d(TAG, "onVerificationCompleted:$credential")

                        if (continuation.isActive) {
                            continuation.resume(AuthFirebaseResult.PhoneNumberVerified(credential) )
                        }
                    }

                    override fun onVerificationFailed(e: FirebaseException) {
                        Log.w(TAG, "onVerificationFailed", e)
                        if (continuation.isActive) {
                            continuation.resume(AuthFirebaseResult.Exception(e))
                        }
                    }

                     override fun onCodeSent(
                         verificationId: String,
                         token: PhoneAuthProvider.ForceResendingToken,
                     ) {
                         Log.d(TAG, "onCodeSent:$verificationId")
                         if (continuation.isActive) {
                             continuation.resume(AuthFirebaseResult.VerificationCodeSent(verificationId))
                         }
                     }
                })
                .build()

            verifyPhoneNumber(options)

            continuation.invokeOnCancellation {
                // Handle the cancellation if necessary
            }
        }


    override suspend fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential): NetworkResult<AuthResult> =
        suspendCancellableCoroutine { continuation ->
            auth.signInWithCredential(credential)
                .addOnCompleteListener { task ->
                    if (task.isSuccessful) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d("TAG", "signInWithCredential:success")

                        val user = task.result?.user

                        Log.d("user", user.toString())
                        continuation.resume(NetworkResult.Success(task.result!!))
                    } else {
                        // Sign in failed, display a message and update the UI
                        Log.w("TAG", "signInWithCredential:failure", task.exception)
                        if (task.exception is FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                            continuation.resume(
                                NetworkResult.Exception(
                                    (task.exception as FirebaseAuthInvalidCredentialsException).cause
                                        ?: Exception()
                                )
                            )
                        }
                    }
                }
        }

    override suspend fun getCredential(verificationId: String, code: String): NetworkResult<PhoneAuthCredential> {
        return try {
            val credential = PhoneAuthProvider.getCredential(verificationId, code)
            NetworkResult.Success(credential)
        } catch (e: Exception) {
            NetworkResult.Exception(e)
        }
    }
}

I understand that the reference to an Activity is required for opening the Recaptcha flow in Firebase Phone Authentication. However, I don’t need Recaptcha since I've already added both SHA-256 and SHA-1 to my Firebase project. What should I do in this case? Should I pass an Activity reference to the ViewModel? That seems like a poor choice.

I tried inserting the link in viewModel and got the following error:

[SmsRetrieverHelper] SMS validation code request failed: unknown status code: 17499 Error Code:39
2024-08-22 16:40:42.831 7414-7414 ContentValues com.example.poputka W onVerificationFailed (Ask Gemini)
                                                                                                    com.google.firebase.FirebaseException: An internal error occurred. [ Error Code:39 ]

Solution

  • As per Firebase docs; The activity parameter is optional. However, if the activity is not set and reCAPTCHA verification is attempted, a FirebaseAuthMissingActivityForRecaptchaException is thrown, which can be handled in the onVerificationFailed callback.

    val options = PhoneAuthOptions.newBuilder(auth)
    .setPhoneNumber(phoneNumber) // Phone number to verify
    .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
    .setActivity(this) // Activity (for callback binding)
    .setCallbacks(callbacks) // OnVerificationStateChangedCallbacks
    .build()
    PhoneAuthProvider.verifyPhoneNumber(options)
    

    https://firebase.google.com/docs/auth/android/phone-auth