{"record":{"id":"d2568c6899845cef","repo":"microg/GmsCore","slug":"no-recaptcha-token-available","errorCode":null,"errorMessage":"No recaptcha token available","messagePattern":"No recaptcha token available","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"firebase-auth/core/src/main/kotlin/org/microg/gms/firebase/auth/FirebaseAuthService.kt","lineNumber":376,"sourceCode":"                Log.w(TAG, \"callback: onFailure\", e)\n                callbacks.onFailure(Status(CommonStatusCodes.INTERNAL_ERROR, e.message))\n            }\n        }\n    }\n\n    override fun sendEmailVerificationCompat(token: String?, actionCodeSettings: ActionCodeSettings?, callbacks: IFirebaseAuthCallbacks) {\n        sendEmailVerification(SendEmailVerificationWithSettingsAidlRequest().apply { this.token = token; this.settings = actionCodeSettings }, callbacks)\n    }\n\n    override fun sendVerificationCode(request: SendVerificationCodeAidlRequest, callbacks: IFirebaseAuthCallbacks) {\n        lifecycleScope.launchWhenStarted {\n            try {\n                Log.d(TAG, \"sendVerificationCode\")\n                val reCaptchaToken = when {\n                    request.request.recaptchaToken != null -> request.request.recaptchaToken\n                    ReCaptchaOverlayService.isSupported(context) -> ReCaptchaOverlayService.awaitToken(context, apiKey, getAuthorizedDomain())\n                    ReCaptchaActivity.isSupported(context) -> ReCaptchaActivity.awaitToken(context, apiKey, getAuthorizedDomain())\n                    else -> throw RuntimeException(\"No recaptcha token available\")\n                }\n                var sessionInfo: String? = null\n                var registered = true\n                val receiver = object : BroadcastReceiver() {\n                    override fun onReceive(context: Context, intent: Intent) {\n                        var smsCode: String? = null\n                        for (message in intent.getSmsMessages()) {\n                            smsCode = Regex(\"\\\\b([0-9]{6})\\\\b\").find(message.messageBody)?.groups?.get(1)?.value\n                                    ?: continue\n                            Log.d(TAG, \"Received SMS verification code: $smsCode\")\n                            break\n                        }\n                        if (smsCode == null) return\n                        registered = false\n                        context.unregisterReceiver(this)\n                        try {\n                            callbacks.onVerificationCompletedResponse(PhoneAuthCredential().apply {\n                                this.phoneNumber = request.request.phoneNumber","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/firebase-auth/core/src/main/kotlin/org/microg/gms/firebase/auth/FirebaseAuthService.kt#L358-L394","documentation":"FirebaseAuthService requires a reCAPTCHA token before sending an SMS verification code (phone auth). It resolves the token from, in order: the client-supplied request.recaptchaToken, the ReCaptchaOverlayService, or the legacy ReCaptchaActivity. If none is available it throws RuntimeException(\"No recaptcha token available\"), meaning phone verification cannot proceed because Google's backend requires proof the request comes from a verified app.","triggerScenarios":"Calling sendVerificationCode (via sendVerificationCodeCompat) when: the request carries no recaptchaToken, the device/app cannot display the ReCaptcha overlay (unsupported context or missing overlay support), and ReCaptchaActivity-based flow is also unsupported (no suitable Activity to launch, e.g. caller lacks activity context or anti-abuse flow disabled).","commonSituations":"A client app calling the microG phone-auth endpoint without obtaining a reCAPTCHA token first; running on a device/ROM where the overlay service can't be bound; background caller (Service/WorkManager) with no foreground Activity, so the interactive reCAPTCHA challenge can't be shown; microG version where both reCAPTCHA paths report isSupported=false.","solutions":["Supply a valid recaptchaToken in the verification request obtained from the reCAPTCHA/Play Integrity verification flow for your app","Call sendVerificationCode from a foreground Activity context so ReCaptchaActivity/overlay can present the challenge","Verify microG is up to date and that ReCaptchaOverlayService/ReCaptchaActivity.isSupported(context) is true on the device","Ensure the app's package name and signature are registered with the Firebase project so the authorized-domain check passes and token retrieval works"],"exampleFix":"// before: requesting code with no token, from background\nval resp = auth.sendVerificationCodeCompat(request)\n// after: fetch a token first and attach it\nval token = ReCaptchaOverlayService.awaitToken(context, apiKey, authorizedDomain)\nrequest.request.recaptchaToken = token\nval resp = auth.sendVerificationCodeCompat(request)","handlingStrategy":"fallback","validationCode":"fun canSendVerificationCode(context: Context): Boolean =\n  ReCaptchaOverlayService.isSupported(context) || ReCaptchaActivity.isSupported(context)","typeGuard":null,"tryCatchPattern":"try {\n  auth.sendVerificationCodeCompat(request)\n} catch (e: RuntimeException) {\n  if (e.message == \"No recaptcha token available\") {\n    // obtain a token via ReCaptchaOverlayService/ReCaptchaActivity, attach it, retry once\n  } else throw e\n}","preventionTips":["Always call phone-auth verification from a foreground Activity so an interactive reCAPTCHA challenge can display","Pre-fetch and attach a recaptchaToken when running from background components","Keep microG and the client SDK updated; check isSupported() before initiating phone verification","Register the app's package name and signature with the Firebase project so token flows succeed"],"tags":["android","firebase","recaptcha","phone-auth"],"backgroundTag":"authentication-required","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}