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 ]
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)