How I Would Fix exposed API keys and missing auth in a Flutter and Firebase internal admin app Using Launch Ready.
If I opened an internal admin app and found exposed API keys plus screens that load without auth, I would treat it as a production security incident, not...
How I Would Fix exposed API keys and missing auth in a Flutter and Firebase internal admin app Using Launch Ready
If I opened an internal admin app and found exposed API keys plus screens that load without auth, I would treat it as a production security incident, not a UI bug. The most likely root cause is simple: the team shipped Firebase client config as if it were secret, then relied on "internal" access instead of real authentication and Firestore/Storage rules.
The first thing I would inspect is whether the app is actually enforcing auth at the data layer, not just hiding buttons in the UI. In practice, that means checking Firebase Auth state, Firestore rules, Storage rules, and any callable functions or REST endpoints the Flutter app talks to.
Triage in the First Hour
1. Confirm what is exposed.
- Check the Flutter repo for hardcoded API keys, service account JSON, private endpoints, and admin URLs.
- Search for `AIza`, `serviceAccount`, `Bearer`, `.env`, and copied Firebase config files.
- Verify whether the keys are Firebase web config only or something truly sensitive.
2. Check whether auth is real or cosmetic.
- Open the app in a clean browser session and on a fresh device.
- Try every admin screen without signing in.
- Confirm whether data loads because of permissive Firestore rules or because the UI simply forgot to gate routes.
3. Inspect Firebase Security Rules first.
- Review Firestore rules, Storage rules, Realtime Database rules if used, and any App Check setup.
- Look for `allow read, write: if true;` or broad wildcard access.
- Check whether roles are enforced by custom claims or only by client-side checks.
4. Review logs and audit trails.
- Inspect Firebase Auth logs, Cloud Logging, and any function logs for anonymous access patterns.
- Look for spikes in reads, writes, or unexpected IPs after deployment.
- Check whether failed auth attempts are being logged at all.
5. Verify deployment scope.
- Confirm which environment is live: dev, staging, or prod.
- Check whether test data and production credentials are mixed together.
- Review recent builds to see when the insecure version went out.
6. Audit secrets exposure paths.
- Inspect Git history for committed `.env`, JSON credentials, or pasted API tokens.
- Check CI/CD variables and hosting settings for accidental disclosure.
- Rotate anything that could be reused outside Firebase client config.
7. Freeze risky changes while you assess impact.
- Pause new releases if possible.
- Disable write access temporarily if unauthorized updates are possible.
- If there is evidence of misuse, rotate secrets before doing anything else.
## Quick rule audit from repo root grep -RniE "allow (read|write): if true|serviceAccount|AIza|Bearer|admin" .
Root Causes
1. Firebase client config was treated as a secret.
- Confirmation: you find API keys in Flutter code or env files and assume they should be hidden from users.
- Reality check: Firebase web API keys are not secrets by themselves, but they still must be paired with strong rules and auth.
2. Firestore or Storage rules are too open.
- Confirmation: anyone can read/write from an unauthenticated session.
- Look for broad allow statements, missing `request.auth != null`, or no role checks.
3. Admin authorization exists only in the UI.
- Confirmation: buttons disappear for non-admin users, but direct route access still works.
- If backend reads succeed without checking claims or roles, the app is insecure even if the UI looks locked down.
4. Custom claims were never set or never checked correctly.
- Confirmation: users sign in but still get full access because role-based logic is missing or stale.
- Check whether claims are assigned during onboarding and refreshed after login.
5. A test project was promoted to production by mistake.
- Confirmation: development rules, debug flags, sample data, or emulator assumptions are present in live builds.
- This often happens when founders move fast with FlutterFire setup and skip environment separation.
6. Backend endpoints trust the client too much.
- Confirmation: Cloud Functions or APIs accept requests without verifying Firebase ID tokens or roles server-side.
- This creates a second path around your front-end protections.
The Fix Plan
My goal here is to make the smallest safe change that closes the hole without breaking admin workflows. I would not start by rewriting the app; I would lock down identity first, then clean up exposure paths one by one.
1. Separate what is public from what must be protected.
- Keep Firebase client config in the app if needed for normal initialization.
- Remove any real secrets from Flutter code immediately:
service account JSON, private API tokens, SMTP passwords, third-party admin credentials, unrestricted database credentials.
2. Enforce authentication at the backend boundary.
- Require Firebase Auth sign-in before any protected read/write path succeeds.
- Use custom claims such as `admin: true` for internal admin users only.
- Validate those claims in Firestore rules and any Cloud Functions logic.
3. Tighten Firestore rules with least privilege.
- Allow public access only where there is a documented business reason.
- Scope reads/writes to specific collections and user roles.
- Deny everything else by default.
4. Lock down Storage rules too.
- Internal apps often forget file uploads because the main bug shows up in Firestore first.
- Protect uploads by user identity and file path ownership where relevant.
5. Move sensitive operations server-side if needed.
- If the app currently performs privileged actions directly from Flutter, move them into callable functions or a protected backend endpoint.
- The client should request an action; the server should decide whether it can happen.
6. Rotate exposed credentials safely.
- Regenerate any compromised third-party keys immediately after confirming scope of exposure.
- Update environment variables in hosting and CI/CD systems only after old values are revoked.
7. Add App Check where appropriate. + For Firebase-backed apps with abuse risk, App Check adds another layer against scripted abuse from untrusted clients: it does not replace auth, but it reduces casual misuse of your backend resources.
8. Clean up route protection in Flutter UX flow: login screen first, loading state while auth resolves, redirect unauthenticated users away from admin routes, friendly error screen when permissions fail, no silent blank pages that look like bugs.
9. Add observability before redeploying widely: log denied requests, log role failures, alert on unusual read/write spikes, monitor function errors, track auth conversion from login screen to successful session.
Here is how I would think about it operationally:
Regression Tests Before Redeploy
I would not ship this fix until denial behavior has been tested as carefully as success behavior. Security regressions usually happen because teams test happy paths only once they feel relieved.
Acceptance criteria:
- Unauthenticated users cannot read protected Firestore collections.
- Unauthenticated users cannot create, update, or delete protected records.
- Non-admin authenticated users cannot access admin routes or privileged data sets.
- Admin users can still complete their normal workflows end to end on mobile and web if applicable.
- No secret values appear in Flutter source, build output, logs, crash reports, or CI artifacts.
QA checks I would run:
1. Fresh-session browser test
- Clear cookies and local storage.
- Open every admin route directly by URL path.
- Confirm redirect to login or blocked state every time.
2. Role matrix test
- Test at least three identities:
unauthenticated, authenticated non-admin, authenticated admin。 Use each against critical reads and writes.
3. Rules test suite
- Use Firebase Emulator Suite to validate allow/deny cases before touching production again.
+ Cover edge cases like empty docs, malformed IDs, cross-user document access, upload attempts outside allowed paths.
4. Build scan + Search final web/mobile artifacts for secret strings before release candidate approval。
5. Error-path testing + Force expired tokens、 revoked sessions、 network failures、 permission denied responses。 + Make sure users see clear messages instead of broken screens。
6. Smoke test after deploy + Verify login、 dashboard load、 create/update/delete flows、 sign out、 token refresh、 role-based redirects。 + Watch logs for 30 to 60 minutes after release。
If I were writing a release gate for this kind of fix, I would require at least 90 percent coverage on security-critical rule tests and zero known open-write paths before production approval.
Prevention
This problem usually comes back when founders rely on speed over structure after launch pressure eases off. I would put guardrails around identity, data access, review process, and monitoring so one rushed commit does not reopen the same hole later.
Guardrails I recommend:
- Make security rules part of code review every time someone touches data access logic?
- Keep production secrets out of Flutter code entirely except public client config where required?
- Use separate Firebase projects for dev, staging?and prod?
- Require emulator-based tests for every rule change?
- Add alerts for abnormal reads,writes,and auth failures?
- Log authorization denials with enough detail to investigate but without exposing sensitive data?
- Review custom claims management whenever onboarding/admin roles change?
- Put App Check on the roadmap once core auth is stable?
For internal admin apps specifically,I also recommend reducing exposed surface area: fewer routes, fewer privileged actions per screen, clearer role boundaries, and stronger defaults on empty states so unauthenticated users never see useful data placeholders that hint at structure。
From a performance angle,the fix should not slow normal use: auth resolution should complete quickly, protected screens should show loading states within 200 ms of route change, and backend checks should keep p95 latency under 300 ms for standard reads where possible。
When to Use Launch Ready
Launch Ready fits when you already have a working Flutter/Firebase product,but deployment safety is weak,and you need it cleaned up fast without dragging this into a multi-week rebuild。I built this sprint for exactly that kind of situation: exposed secrets,messy environment setup,and missing production controls that can block launch or create support risk。
domain,email setup via Cloudflare,DNS redirects,and subdomains; SSL,caching,and DDoS protection; SPF,DKIM,and DMARC; production deployment; environment variables; secret handling; uptime monitoring; and a handover checklist。
What you should prepare before booking: Firebase project access with owner-level permissions; Flutter repo access; hosting provider access; list of current environments; any known admin accounts; recent build links; and a short note on which screens must stay private。
If your issue includes both exposed keys and missing auth,I would usually start with Launch Ready only if you need immediate deployment safety plus handoff discipline。If there is active abuse,data leakage,isolation failure between tenants,etc.,I would scope a security rescue sprint first,and then use Launch Ready once the app is safe enough to go live。
References
- https://roadmap.sh/api-security-best-practices
- https://roadmap.sh/cyber-security
- https://firebase.google.com/docs/rules
- https://firebase.google.com/docs/auth
- https://firebase.google.com/docs/app-check
---
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.