fixes / launch-ready

How I Would Fix exposed API keys and missing auth in a Flutter and Firebase paid acquisition funnel Using Launch Ready.

The symptom is usually obvious: a paid traffic funnel starts working, but the backend is wide open. You find Firebase API keys in the app bundle or repo,...

How I Would Fix exposed API keys and missing auth in a Flutter and Firebase paid acquisition funnel Using Launch Ready

The symptom is usually obvious: a paid traffic funnel starts working, but the backend is wide open. You find Firebase API keys in the app bundle or repo, endpoints that accept requests without verifying identity, and users can hit premium flows without paying or signing in.

The most likely root cause is not "Firebase is insecure." It is that the app was shipped with client-side trust assumptions. The first thing I would inspect is the actual auth boundary: Firebase Auth rules, Firestore/Storage rules, Cloud Functions or callable functions, and whether any sensitive action can be triggered from the Flutter client without server-side verification.

Triage in the First Hour

I would spend the first hour proving where the trust break is, not guessing.

1. Check the live funnel path from ad click to payment to gated access.

  • Confirm which screens are public.
  • Confirm which screens should require login or purchase.
  • Look for broken redirects after checkout.

2. Inspect Firebase Auth state in the app.

  • Is anonymous access enabled?
  • Are users getting a valid ID token before sensitive calls?
  • Does the app proceed when auth fails?

3. Review Firestore and Storage security rules.

  • Look for `allow read, write: if true;`
  • Look for rules that only check `request.auth != null` when paid access should require more than login.

4. Audit Cloud Functions and API endpoints.

  • Identify any function that trusts client-provided user IDs, plan names, or entitlement flags.
  • Check whether callable functions verify `context.auth`.

5. Search for exposed secrets in source control and build artifacts.

  • `.env`, `google-services.json`, `GoogleService-Info.plist`
  • Firebase web config
  • Stripe keys, OpenAI keys, resend/mailgun keys, Supabase keys if present

6. Review deployment history.

  • Last successful build
  • Recent hotfixes
  • Any manual edits in Firebase console or Cloudflare

7. Check logs and dashboards.

  • Firebase Auth logs
  • Cloud Functions logs
  • Crashlytics if enabled
  • Hosting/CDN logs if traffic spikes are happening

8. Verify whether the key exposure is actually dangerous.

  • Firebase web API key exposure alone is often not the real issue.
  • The real problem is usually weak rules, missing auth checks, or privileged secrets shipped to the client.

A quick diagnostic command I would run during triage:

grep -R "allow read\|allow write\|apiKey\|secret\|stripe\|openai" .

Root Causes

Here are the most likely causes I see in Flutter plus Firebase funnels, and how I confirm each one.

| Likely cause | What it looks like | How I confirm it | |---|---|---| | Weak Firestore rules | Anyone can read/write collections | Test with an unauthenticated request and review rules simulator | | Missing entitlement checks | Logged-in users get premium access without payment | Trace purchase state from checkout to gated screen | | Secrets bundled in Flutter | Keys visible in app code or decompiled assets | Search repo and inspect build outputs | | Client-trusted backend logic | App sends `isPaid=true` or user role from UI | Review Cloud Functions and request payloads | | Callable functions missing auth validation | Function works without a verified user token | Inspect `context.auth` handling and logs | | Misconfigured environment separation | Dev keys used in prod or vice versa | Compare Firebase projects, env files, and release config |

1. Weak Firestore or Storage rules

This is common when a founder ships fast and leaves default permissive rules behind. If a collection holds leads, payments, user profiles, or gated content metadata, open rules can expose customer data or let attackers tamper with records.

I confirm this by reading the active rules in Firebase console and testing unauthenticated access against specific documents and paths. If a stranger can read lead data or write entitlement flags, that is a production incident.

2. Missing server-side entitlement checks

A paid acquisition funnel needs one hard rule: purchase state must be verified on the server before premium access is granted. If the Flutter app decides "this user is paid" based on local storage or a client flag, someone can bypass it.

I confirm this by tracing how access changes after checkout. If there is no server-issued entitlement record tied to a verified payment event, I treat it as broken by design.

3. Secrets shipped inside the app

Flutter apps can hide secrets poorly if someone puts them into constants files or environment-like configs that get compiled into the binary. A public Firebase config object is normal; private API keys are not.

I confirm this by searching source code plus build artifacts for anything that should never be public. If an admin key exists in mobile code, I assume it has already been compromised.

4. Backend trusts client input too much

If Cloud Functions accept `userId`, `plan`, `email`, or `role` from the app without cross-checking against authenticated identity and payment records, then anyone can forge requests. This becomes expensive fast because it turns into support tickets, refund disputes, and data cleanup work.

I confirm this by reviewing function inputs versus server-derived identity. The server should derive trust from Firebase ID tokens and payment webhooks, not from UI values.

5. Separate environments were never set up cleanly

A lot of teams accidentally point staging builds at production Firebase projects or reuse secrets across environments. That makes debugging impossible because test traffic can mutate production data.

I confirm this by comparing Android/iOS flavors, Flutter compile-time variables, Firebase project IDs, hosting targets, and deployed function aliases.

The Fix Plan

My approach is to stop damage first, then repair access control at the boundary where it belongs.

1. Freeze risky paths.

  • Temporarily disable any endpoint or function that writes entitlements without verification.
  • If needed, lock down affected Firestore collections while preserving read-only public content.

2. Rotate exposed secrets immediately.

  • Revoke any leaked third-party API keys.
  • Replace them with new secrets stored only server-side.
  • Assume anything exposed in a client bundle has been copied already.

3. Move all privileged logic off the Flutter client.

  • Payment status checks belong on Cloud Functions or your backend.
  • Premium access should come from a server-written entitlement record after webhook verification.
  • The app should only display state returned by trusted sources.

4. Tighten Firebase Security Rules.

  • Require authentication for private collections.
  • Restrict writes to ownership-based paths where possible.
  • Deny everything by default for sensitive data.

5. Add callable function auth checks.

  • Verify `context.auth != null`.
  • Cross-check UID against database ownership fields.
  • Reject requests that try to self-assign roles or entitlements.

6. Separate public marketing data from private product data.

  • Keep landing page content public if needed for conversion.
  • Move leads, purchases, receipts, and user records into locked collections with minimal access paths.

7. Clean up configuration management.

  • Use `.env` only for non-sensitive compile-time values where appropriate.
  • Store real secrets in server-side secret managers or Cloud Functions config equivalents.
  • Maintain separate dev/staging/prod projects.

8. Add monitoring before reopening traffic fully.

  • Alert on auth failures spikes

to catch broken flows early.

  • Alert on unusual reads/writes to protected collections.
  • Watch conversion drop-off after redeploy so you do not silently break paid acquisition performance.

Here is the decision path I would follow:

The main trade-off here is speed versus safety. For a paid funnel with live ad spend running through it, I recommend stopping revenue leakage first even if it means a short pause on campaigns for 12 to 24 hours.

Regression Tests Before Redeploy

I would not redeploy until these checks pass end to end.

  • Unauthenticated user cannot read private Firestore documents.
  • Unauthenticated user cannot write entitlements or premium flags.
  • Authenticated free user cannot open premium screens after refresh/reinstall.
  • Paid user regains access after valid webhook processing only.
  • Revoked subscription loses access within an acceptable window, ideally under 5 minutes if event-driven.
  • No secret appears in repo search results or built assets.
  • App launches cleanly on iOS and Android with correct environment target selected.
  • Checkout completion redirects correctly back into gated flow on mobile webviews if used.

Acceptance criteria I would use:

  • Zero unauthorized reads of protected collections during testing.
  • Zero unauthorized writes accepted by backend endpoints.
  • Premium gating works after logout/login cycle and app reinstall cycle.
  • No critical errors in Crashlytics or function logs during smoke test window of 30 minutes.
  • Funnel step completion rate stays within 10 percent of baseline after fix deployment unless we intentionally changed UX.

I also want one small red-team style check set before launch:

  • Try empty token requests against protected functions.
  • Try stale token requests after sign-out.
  • Try tampered entitlement payloads from client storage.
  • Try replaying old payment success events to ensure idempotency holds.

Prevention

This issue comes back when teams ship faster than they secure their boundary layers. I would put guardrails around code review, deployment, QA, and monitoring so one bad merge does not expose paying customers again.

Security guardrails

  • Require review for every rule change in Firestore/Storage/Firebase Auth settings site-wide changes count as code changes in practice because they affect behavior directly.
  • Keep all privileged operations behind server-side verification of auth plus payment status.
  • Rotate secrets on schedule and immediately after any suspected exposure event unless you have proof they were never accessible outside trusted systems anyway could be wrong due to unknown factors? Wait no let's keep clean; remove uncertainty phrase? Need final answer only body no note maybe fine but let's avoid oddity.</final>

References

  • [roadmap.sh - API security](https://roadmap.sh/api-security-best-practices)
  • [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
  • [MDN Web Docs - HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
  • [Cloudflare DNS documentation](https://developers.cloudflare.com/dns/)
  • [Sentry documentation](https://docs.sentry.io/)

---

Take the next step

If this is a problem in your product right now, here is what to do next:

  • [Use the free Cyprian tools](/tools) - estimate cost, score app risk, check launch readiness, or pick the right service sprint.
  • [Book a discovery call](/contact) - I will tell you honestly whether you need a sprint or if you can DIY the next step.

*Written by Cyprian Tinashe Aarons - senior full-stack and AI engineer helping founders rescue, launch, automate, and scale AI-built products.*

Next steps
About the author

Cyprian Tinashe AaronsSenior Full Stack & AI Engineer

Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.