How I Would Fix exposed API keys and missing auth in a Flutter and Firebase subscription dashboard Using Launch Ready.
The symptom is usually simple to spot: the app works, but anyone who opens the client bundle, inspects network calls, or hits Firebase endpoints directly...
How I Would Fix exposed API keys and missing auth in a Flutter and Firebase subscription dashboard Using Launch Ready
The symptom is usually simple to spot: the app works, but anyone who opens the client bundle, inspects network calls, or hits Firebase endpoints directly can see API keys, read data they should not see, or trigger actions without being signed in. In a subscription dashboard, that means leaked customer data, broken billing trust, support tickets, and a real risk of abuse before you even notice.
The most likely root cause is not "a hacked app". It is usually a weak Firebase security model plus secrets shipped in the Flutter client as if they were private. The first thing I would inspect is Firestore or Realtime Database rules, then the auth flow in Flutter, then any exposed config in the web build, mobile build, or environment files.
Triage in the First Hour
1. Check Firebase Authentication status.
- Confirm whether anonymous users can load dashboard screens.
- Verify whether sign-in is required before any subscription data query runs.
- Look for missing session checks on app startup and route guards.
2. Inspect Firestore or Realtime Database rules.
- Open the rules tab and confirm if `allow read, write: if true` exists anywhere.
- Check whether rules depend on `request.auth != null`.
- Verify whether user-owned records are scoped by UID or tenant ID.
3. Review exposed config in Flutter.
- Search for hardcoded API keys, project IDs, service URLs, Stripe keys, or admin tokens.
- Check `.env`, `firebase_options.dart`, build scripts, and any committed sample files.
- Confirm no secret was baked into the web bundle or checked into Git history.
4. Audit Firebase console access.
- Confirm who has Owner, Editor, or Service Account permissions.
- Remove stale collaborators and rotate anything that may have been copied out.
- Check audit logs for unusual rule changes or key creation.
5. Inspect hosting and deployment settings.
- Verify production vs staging environments are not mixed.
- Check Cloudflare, domain redirects, SSL status, and cache headers if the app is web-based.
- Confirm the deployed build matches the intended branch and environment variables.
6. Review recent crash logs and analytics.
- Look for auth-related errors like permission denied or unauthenticated reads.
- Check whether many requests are coming from one IP or one device pattern.
- Note any sudden spike in reads, writes, or failed sign-ins.
7. Validate billing and subscription gates.
- Confirm premium screens are protected by server-side logic or trusted claims.
- Make sure UI-only paywall checks are not being used as security controls.
- Verify canceled users cannot still access paid data through direct API calls.
## Quick sanity check for risky patterns grep -RInE "allow (read|write): if true|apiKey|serviceAccount|secret|token|Bearer" lib firebase . --exclude-dir=build
Root Causes
1. Public Firebase config was treated like a secret.
- Firebase web config is often public by design, but teams still paste privileged values next to it.
- Confirm by checking `firebase_options.dart`, `.env`, CI variables, and repo history for anything that grants admin access.
2. Firestore rules are too open.
- If unauthenticated users can read subscription documents, the rules are wrong even if the UI hides them.
- Confirm by testing with a logged-out browser session and reading documents directly from Firebase tools or app requests.
3. Missing auth guard on app routes and data fetches.
- The app may render dashboard pages before auth state finishes loading.
- Confirm by disabling cookies/session storage and seeing whether protected screens still mount or fetch data.
4. Client-side only subscription checks.
- Some apps show "premium" content based on local flags instead of server-verified claims or backend validation.
- Confirm by editing local storage or bypassing UI paths and seeing whether protected content still loads.
5. Overprivileged service account or admin SDK usage in the client path.
- If an admin credential was accidentally shipped through a callable function misuse or bad environment setup, exposure becomes serious fast.
- Confirm by reviewing where privileged actions run: only server code should touch admin credentials.
6. Weak deployment hygiene copied secrets into multiple environments.
- Staging keys often get promoted into production builds by mistake during rushed releases.
- Confirm by comparing production env vars, build artifacts, Cloudflare settings, and mobile/web release pipelines.
The Fix Plan
My recommendation is to stop shipping new builds until the security boundary is corrected. Do not patch only the UI; fix authentication first, then lock down database access, then rotate anything exposed.
1. Freeze access paths that expose data now.
- Temporarily tighten Firestore/Realtime Database rules to deny unauthenticated access where safe to do so.
- If needed, disable risky endpoints until the new rules are validated.
2. Separate public config from private secrets.
- Keep Firebase web config public only if it contains non-sensitive identifiers required by the client SDK.
- Move all privileged operations to Cloud Functions or a backend service with least privilege.
3. Enforce auth before any dashboard query runs.
- In Flutter, gate protected routes behind an auth state listener.
- Block data fetching until `currentUser` is present and token refresh has completed.
4. Rewrite database rules around ownership and claims.
- Require `request.auth != null`.
- Scope reads and writes to `request.auth.uid` or verified custom claims for team accounts and admins only.
5. Rotate anything that may have been exposed publicly.
- Rotate API keys that actually grant access outside Firebase public config values.
- Replace compromised secrets in CI/CD immediately and invalidate old versions where possible.
6. Move subscription verification server-side if it is not already there.
- Use a trusted backend source for entitlement checks instead of local flags alone.
- If Stripe is involved, verify webhook events server-side before unlocking premium access.
7. Clean up build artifacts and history risk.
- Remove secrets from Git history if they were committed; at minimum rotate them after removal because history may still be accessible internally or externally depending on repo exposure.
- Purge old builds from hosting platforms so stale bundles are not still served through caches.
8. Add monitoring before re-enabling full traffic.
- Set alerts for permission-denied spikes, unusual read volume, failed auth attempts, and sudden rule change events
- Track p95 response time for dashboard loads so security changes do not silently break performance above 2 seconds on key pages
Here is the rule shape I would aim for as a baseline:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /subscriptions/{docId} {
allow read: if request.auth != null && request.auth.uid == resource.data.userId;
allow write: if false;
}
}
}This is not a full production policy by itself. It shows the direction: authenticated access only, ownership-based reads where appropriate, and no casual client writes to sensitive records.
Regression Tests Before Redeploy
I would not redeploy until these checks pass in staging with fresh accounts and clean browsers.
1. Auth gating test
- Open the app logged out and confirm protected routes redirect to sign-in within 1 second of load completion
- Acceptance criteria: no dashboard data appears before auth state resolves
2. Unauthorized read test
- Try reading another user's subscription record from a second account
- Acceptance criteria: request fails with permission denied every time
3. Unauthorized write test - Attempt profile edits, plan changes, or entitlement updates without valid session context - Acceptance criteria: writes fail unless explicitly allowed by policy
4. Secret exposure test - Scan built assets, Flutter env files, and deployment variables for private keys - Acceptance criteria: no admin credential, service account JSON, or private token exists in client bundles
5. Subscription gate test - Cancel, downgrade, and re-login with three separate test accounts - Acceptance criteria: access changes reflect verified backend state within 60 seconds max
6. Error handling test - Kill network, expire token, and force permission errors on key screens - Acceptance criteria: user sees clear recovery messaging, not blank screens or infinite spinners
7. Security smoke test - Review logs for sensitive values accidentally printed during login, billing sync, or rule failures - Acceptance criteria: logs contain no tokens, no raw credentials, and no personal data beyond what is necessary
8. Cross-device check - Test Android, iOS, and web if applicable - Acceptance criteria: identical auth behavior across platforms with no platform-specific bypass
Prevention
The long-term fix is boring discipline around security boundaries. In a Flutter plus Firebase stack, I would put these guardrails in place:
- Use least privilege everywhere:
only authenticated users get access, admins use custom claims, service accounts stay server-side only
- Review every Firestore rule change like code:
one bad rule can expose every customer record instantly
- Keep secrets out of client code:
assume anything shipped to Flutter web can be inspected
- Add CI checks:
block merges if risky patterns like open rules, hardcoded tokens, or admin SDK misuse appear in diffs
- Monitor authentication failures:
sudden spikes often mean broken flows or abuse attempts
- Log safely:
never print tokens, passwords, or full payloads into debug logs that survive into production builds
- Test paywall logic against tampering:
treat UI as presentation only; entitlements must be verified server-side
- Improve UX around auth states:
loading skeletons, clear error messages, and explicit sign-in prompts reduce support load when sessions expire
For performance safety too: keep initial dashboard payloads small, paginate subscriptions lists, and avoid fetching everything on first paint because slow auth plus heavy queries creates bad onboarding friction and higher drop-off.
When to Use Launch Ready
Launch Ready fits when you need me to stop release risk fast rather than spend weeks debating architecture. email deliverability basics, Cloudflare protection, SSL, production deployment choreography, secrets handling, environment variables, uptime monitoring, and a handover checklist so you can launch without guessing what is live versus what is still staging.
I would use this sprint when:
- your Flutter/Firebase app works but feels unsafe to ship
- secrets may already be exposed in builds or repo history
- you need DNS redirects,
subdomains, Cloudflare caching, or DDoS protection cleaned up before traffic lands
- you want a clean production handoff instead of another half-finished deploy
What I need from you before I start:
- repo access with current branch notes
- Firebase project access at least Editor level for implementation work
- hosting access such as Vercel,
Firebase Hosting,
Cloudflare,
or your mobile release pipeline depending on platform
If your product already has paying users,
I would treat this as urgent because every extra day of exposed auth increases support load,
refund risk,
and trust damage with real customers.
Delivery Map
References
- https://roadmap.sh/api-security-best-practices
- https://roadmap.sh/cyber-security
- https://roadmap.sh/qa
- https://firebase.google.com/docs/firestore/security/get-started
- https://firebase.google.com/docs/auth
---
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.*
Cyprian Tinashe Aarons — Senior Full Stack & AI Engineer
Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.