Back to blog

    Fixes & Rescues

    Firebase Phone Auth & OTP Errors in Flutter: The Complete Fix Guide

    SMS codes not arriving, 'app not authorized', reCAPTCHA loops, ERROR_SESSION_EXPIRED — every common Firebase phone verification failure in Flutter, diagnosed and fixed.

    2026-07-159 min readTarget keyword: fix firebase phone verification flutter

    Firebase phone authentication is the feature most likely to work perfectly in development and then break for real users. If your Flutter app's OTP codes aren't arriving, verification throws 'app not authorized', or users are stuck in a reCAPTCHA loop, this guide walks through every common cause in the order we check them when we take over broken apps.

    First, identify which of the three failure types you have

    Almost every Firebase phone auth problem falls into one of three buckets: configuration mismatches (fingerprints, bundle IDs, missing keys), quota and billing limits (SMS simply not being sent), or device-integrity checks failing (Play Integrity, APNs, reCAPTCHA fallbacks).

    The error message usually tells you the bucket. 'App not authorized' and 'missing client identifier' are configuration. Codes that never arrive with no error are usually quota or carrier issues. reCAPTCHA appearing for every user, or web views popping open mid-login, is device integrity. Match your symptom below.

    Fix 1: 'App not authorized' — the SHA fingerprint problem (Android)

    This is the single most common failure, and it has a cruel twist: it appears only in production. Firebase validates your Android app by its signing certificate's SHA-1 and SHA-256 fingerprints, registered in Firebase Console → Project settings → Your apps.

    The twist: the key that signs your local builds is not the key that signs the version in the Play Store. When you use Play App Signing (almost everyone does now), Google re-signs your app with its own key. So phone auth works in debug, works in your release APK, and dies for every Play Store user.

    • Get the production fingerprints from Play Console → Setup → App signing (the 'App signing key certificate' section — not your upload key)

    • Add both SHA-1 and SHA-256 to Firebase Console → Project settings → Your apps → Add fingerprint

    • Download the refreshed google-services.json and rebuild — the old file doesn't know about the new fingerprints

    • Keep the debug fingerprint registered too, or local testing breaks the other way

    Fix 2: codes never arrive — quota, billing, and region blocks

    If there's no error but no SMS, start with billing. On the free Spark plan, Firebase phone auth has a small daily SMS quota; production apps need the Blaze plan, and several newer Firebase projects require billing enabled before any SMS is sent at all. The console error 'BILLING_NOT_ENABLED' says exactly that.

    Next, check regions: Firebase lets you configure allowed SMS regions (Authentication → Settings → SMS region policy), and messages to unlisted countries silently fail. Finally, some carriers — especially in South Asia and parts of Africa — filter automated SMS aggressively; test with numbers on multiple carriers before assuming the code is broken.

    Also watch for 'blocked due to unusual activity': Firebase throttles a device or number after repeated attempts. During your own testing this is almost guaranteed to trigger — use the test phone numbers feature (Authentication → Sign-in method → Phone → test numbers) so your QA doesn't burn real quota or trip abuse detection.

    Fix 3: iOS — APNs is doing more than you think

    On iOS, Firebase verifies devices through a silent APNs push before falling back to reCAPTCHA. If your APNs authentication key isn't uploaded (Firebase Console → Project settings → Cloud Messaging → APNs auth key), or push capabilities aren't enabled in Xcode, every sign-in falls back to a reCAPTCHA web view — ugly, slow, and frequently mistaken for a bug.

    The checklist: APNs key uploaded with correct Team ID, Push Notifications capability enabled, Background Modes → Remote notifications ticked, and the URL scheme from GoogleService-Info.plist (REVERSED_CLIENT_ID) registered so the reCAPTCHA fallback can return to your app instead of dead-ending in Safari.

    Fix 4: the reCAPTCHA loop and Play Integrity

    On Android, Firebase now verifies app integrity through the Play Integrity API (the older SafetyNet path is deprecated — apps still configured for it degrade to reCAPTCHA for everyone). If every user sees a browser window during login, integrity verification is failing.

    Enable the Play Integrity API in Google Cloud Console for your project, confirm the SHA fingerprints from Fix 1 (integrity checks reuse them), and if you use App Check, make sure a debug token is registered for development devices — otherwise your own emulator gets treated as an attacker.

    Fix 5: ERROR_SESSION_EXPIRED and code-entry UX failures

    Some 'bugs' are really UX design gaps around the OTP lifecycle. Verification codes expire (default ~60 seconds for auto-retrieval; the session itself in a few minutes) — if your UI has no countdown and no resend button, slow users hit ERROR_SESSION_EXPIRED and blame the app.

    Build the flow users expect: visible countdown, resend enabled after it lapses (calling verifyPhoneNumber again with a forceResendingToken), and on Android, SMS auto-retrieval wired through the verificationCompleted callback so most users never type the code at all.

    When it's not configuration: the architecture problem underneath

    In apps we take over, about half the 'Firebase auth bug' reports trace to architecture rather than settings: auth logic scattered through UI widgets, no mapping of FirebaseAuthException codes to user-readable messages, no retry strategy, and no alternative sign-in path when SMS fails.

    Production-grade phone auth is wrapped in one auth service class, surfaces every error code as a human sentence, logs failures to Crashlytics with the error code attached, and offers an escape hatch — email link or social login — for the small percentage of users SMS will never reach. If your codebase can't tell you which error code users are hitting, that's the first thing to fix.

    The 15-minute diagnostic checklist

    Run these in order before touching any code — they resolve the majority of cases:

    • Play Console app-signing SHA-1/SHA-256 registered in Firebase, google-services.json re-downloaded after

    • Blaze plan active; SMS region policy allows your users' countries

    • Test phone numbers configured so QA doesn't trip abuse throttling

    • iOS: APNs key uploaded, push capability on, REVERSED_CLIENT_ID URL scheme registered

    • Play Integrity API enabled; App Check debug tokens registered for dev devices

    • OTP screen has countdown, resend with forceResendingToken, and Android auto-retrieval

    • FirebaseAuthException codes logged to Crashlytics so you can see real-user failures

    Founders also ask

    Why does phone login work in debug but not in the released app? Almost always the signing-key mismatch in Fix 1 — the Play Store version is signed by Google's key, whose fingerprint you haven't registered in Firebase. It's a five-minute fix once you know where to look.

    How much does Firebase phone auth cost at scale? SMS pricing is per-message and varies by country — from under a cent to several cents per verification. At thousands of signups a month this becomes a real line item, which is why growing apps often move second-factor flows to cheaper channels and keep SMS only where it genuinely reduces fraud.

    Should I replace phone auth with something else? If SMS delivery problems persist in your key markets, email-link auth or social sign-in (Google/Apple) is more reliable and free. Apple actually requires offering Sign in with Apple if you offer other social logins — worth designing for before your next store submission.

    Can this be fixed without rebuilding my app? Usually yes. Most phone-auth failures are console configuration, not code — and even the architecture fixes are contained to the auth layer. A rescue of this specific problem typically takes days, not weeks.

    Need Help With This?

    Share your current product stage and we’ll suggest the best next step.