Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Improve Your FlutterFlow App's Security

Securing a FlutterFlow app means: replacing default Firestore test rules with strict per-user rules, moving all API keys to Cloud Functions so they never reach the client, enabling Firebase App Check to block fake clients, validating all user inputs server-side, and adding rate limiting on sensitive endpoints. Never deploy with the default 'allow read, write: if true' rules.

What you'll learn

  • How to replace dangerous default Firestore rules with secure per-user access control
  • How to protect API keys by moving them to Firebase Cloud Functions
  • How to enable Firebase App Check to verify legitimate app clients
  • How to validate user inputs and sanitize data before writing to Firestore
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate10 min read45-60 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Securing a FlutterFlow app means: replacing default Firestore test rules with strict per-user rules, moving all API keys to Cloud Functions so they never reach the client, enabling Firebase App Check to block fake clients, validating all user inputs server-side, and adding rate limiting on sensitive endpoints. Never deploy with the default 'allow read, write: if true' rules.

A Practical Security Checklist for FlutterFlow Apps

Most FlutterFlow apps launch with significant security holes — not because builders are careless, but because FlutterFlow's default Firebase setup prioritizes getting something working over locking it down. The number one threat is the default Firestore test rules that allow anyone on the internet to read and write your entire database. This tutorial covers the full security checklist: Firestore rules, API key management, Firebase App Check, input validation, and rate limiting. Each step is actionable in under 10 minutes and dramatically improves your app's security posture.

Prerequisites

  • FlutterFlow project with Firebase connected
  • Firebase console access for your project
  • Firebase Authentication enabled with at least one sign-in method
  • Basic understanding of Firestore collections and documents

Step-by-step guide

1

Replace default Firestore test rules immediately

This is the most critical security fix. In the Firebase console, click Firestore Database in the left sidebar, then click the Rules tab. If you see 'allow read, write: if true;' — your database is completely public. Anyone with your project ID can read, write, and delete all your data. Replace the rules immediately with the template below that restricts all operations to authenticated users accessing only their own data. For collections that need different permissions (like public content or admin-only data), add specific rules above the default deny-all at the bottom.

firestore.rules
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // Users can only read and write their own profile
6 match /users/{userId} {
7 allow read, write: if request.auth != null
8 && request.auth.uid == userId;
9 }
10
11 // Users can read public articles; only authors can write
12 match /articles/{articleId} {
13 allow read: if true;
14 allow create: if request.auth != null;
15 allow update, delete: if request.auth != null
16 && request.auth.uid == resource.data.author_id;
17 }
18
19 // Orders: users see only their own; admins see all
20 match /orders/{orderId} {
21 allow read, write: if request.auth != null
22 && request.auth.uid == resource.data.user_id;
23 allow read, write: if request.auth != null
24 && request.auth.token.admin == true;
25 }
26
27 // Default deny-all for everything else
28 match /{document=**} {
29 allow read, write: if false;
30 }
31 }
32}

Expected result: Unauthenticated requests to your Firestore database return permission-denied errors. Authenticated users can only access documents they are authorized for.

2

Move all API keys to Cloud Functions — never store them in FlutterFlow

FlutterFlow compiles your app into JavaScript (for web) and Dart (for mobile). Any API keys you store in App State variables, Custom Actions, or widget properties are visible in the compiled app bundle — any user can extract them with basic tools. Instead, create a Firebase Cloud Function for each third-party API call (Stripe, SendGrid, OpenAI, etc.). Store API keys as Firebase environment secrets using 'firebase functions:secrets:set MY_API_KEY'. The Cloud Function reads the secret at runtime, makes the API call, and returns only the needed data to the app. In FlutterFlow, call the function using a Custom Action that hits the Cloud Function's HTTPS endpoint.

Expected result: Your app bundle contains zero API keys. All sensitive API calls are proxied through Cloud Functions that read secrets from Firebase's secure environment.

3

Enable Firebase App Check to block fake clients

Firebase App Check verifies that requests to your Firebase services come from your legitimate app and not from curl, Postman, or a competitor's script. In the Firebase console, go to App Check under the Build section. Click 'Get started', select your app (iOS and Android separately), and choose the attestation provider: Play Integrity for Android and App Attest for iOS (both free). On the Web, use reCAPTCHA Enterprise. Once enabled and enforced, only verified app instances can call Firestore, Storage, and Cloud Functions. Enforcement takes effect 7 days after enabling — use the debug token feature during development to test without affecting your real usage metrics.

Expected result: The App Check dashboard shows verified attestation requests from your app. Requests from curl or unauthorized clients receive a 403 App Check token is invalid response.

4

Validate and sanitize all user inputs before writing to Firestore

FlutterFlow's form validation (required fields, email format) only runs on the client. A determined attacker can bypass the app entirely and send malformed data directly to Firestore. Add server-side validation in two places: first, in your Firestore security rules using request.resource.data conditions to enforce field types and lengths; second, in Cloud Functions that handle sensitive writes. Check string lengths (request.resource.data.username.size() < 50), reject special characters in fields that should not have them, and verify numeric fields are within acceptable ranges. Never trust that data stored in Firestore was written by your app.

firestore_validation.rules
1// Firestore rule example: validate article fields on create
2match /articles/{articleId} {
3 allow create: if request.auth != null
4 && request.resource.data.title is string
5 && request.resource.data.title.size() > 0
6 && request.resource.data.title.size() <= 200
7 && request.resource.data.body is string
8 && request.resource.data.body.size() <= 50000
9 && request.resource.data.author_id == request.auth.uid
10 && request.resource.data.keys().hasOnly(
11 ['title', 'body', 'tags', 'author_id', 'created_at']);
12}

Expected result: Firestore rejects documents that fail validation — wrong field types, oversized strings, or unexpected extra fields — even when submitted directly without going through your app.

5

Add rate limiting on sensitive Cloud Functions

Without rate limiting, any authenticated user can hammer your Cloud Functions thousands of times — running up your Firebase bill or spamming other users. Implement rate limiting using Firestore to track request counts per user. In your Cloud Function, before processing the request, check a rate_limits/{userId} document. If the request count in the last minute exceeds your threshold, return a rate limit error immediately. Use FieldValue.increment to update the counter atomically and set a Firestore TTL to auto-delete old rate limit documents. For RapidDev clients building payment apps, we recommend 10 requests per minute for financial Cloud Functions and 60 per minute for general data operations.

rate_limiter.js
1// Cloud Function rate limiter middleware
2const admin = require('firebase-admin');
3
4async function checkRateLimit(userId, actionName, maxPerMinute = 20) {
5 const db = admin.firestore();
6 const key = `${userId}_${actionName}`;
7 const ref = db.collection('rate_limits').doc(key);
8
9 const now = Date.now();
10 const windowStart = now - 60 * 1000;
11
12 const result = await db.runTransaction(async (tx) => {
13 const doc = await tx.get(ref);
14 const data = doc.exists ? doc.data() : { count: 0, windowStart: now };
15
16 // Reset window if older than 1 minute
17 if (data.windowStart < windowStart) {
18 tx.set(ref, { count: 1, windowStart: now, expiresAt: new Date(now + 120000) });
19 return { allowed: true };
20 }
21
22 if (data.count >= maxPerMinute) {
23 return { allowed: false };
24 }
25
26 tx.update(ref, { count: admin.firestore.FieldValue.increment(1) });
27 return { allowed: true };
28 });
29
30 return result.allowed;
31}

Expected result: Calling a rate-limited Cloud Function more than 20 times per minute returns a resource-exhausted error. Normal users are never affected because they naturally stay within the limit.

Complete working example

secure_firestore_rules_template.rules
1rules_version = '2';
2service cloud.firestore {
3 match /databases/{database}/documents {
4
5 // Helper functions
6 function isSignedIn() {
7 return request.auth != null;
8 }
9 function isOwner(userId) {
10 return isSignedIn() && request.auth.uid == userId;
11 }
12 function isAdmin() {
13 return isSignedIn() && request.auth.token.admin == true;
14 }
15 function validString(field, maxLen) {
16 return field is string && field.size() > 0 && field.size() <= maxLen;
17 }
18
19 // User profiles
20 match /users/{userId} {
21 allow read: if isSignedIn();
22 allow create: if isOwner(userId)
23 && validString(request.resource.data.display_name, 100)
24 && request.resource.data.keys().hasOnly(
25 ['display_name', 'email', 'created_at', 'avatar_url']);
26 allow update: if isOwner(userId)
27 && validString(request.resource.data.display_name, 100);
28 allow delete: if isOwner(userId) || isAdmin();
29 }
30
31 // Public content — readable by all, writable by author
32 match /articles/{articleId} {
33 allow read: if true;
34 allow create: if isSignedIn()
35 && validString(request.resource.data.title, 200)
36 && request.resource.data.author_id == request.auth.uid
37 && request.resource.data.keys().hasOnly(
38 ['title', 'body', 'tags', 'author_id', 'created_at', 'status']);
39 allow update: if isOwner(resource.data.author_id) || isAdmin();
40 allow delete: if isOwner(resource.data.author_id) || isAdmin();
41 }
42
43 // Orders — private to owner
44 match /orders/{orderId} {
45 allow read, write: if isOwner(resource.data.user_id);
46 allow read, write: if isAdmin();
47 }
48
49 // Admin-only collections
50 match /admin_settings/{docId} {
51 allow read, write: if isAdmin();
52 }
53
54 // Default deny-all
55 match /{document=**} {
56 allow read, write: if false;
57 }
58 }
59}

Common mistakes when improving Your FlutterFlow App's Security

Why it's a problem: Deploying with the default Firestore test rules (allow read, write: if true)

How to avoid: Replace test rules immediately with authentication-based rules. Go to Firebase console → Firestore → Rules and deploy the secure template from this tutorial before your first real user signs up.

Why it's a problem: Storing third-party API keys in FlutterFlow App State or Custom Action code

How to avoid: Store all API keys as Firebase Function secrets and proxy every third-party API call through a Cloud Function. The function returns only the data the app needs, never the raw API key.

Why it's a problem: Relying only on client-side form validation for security

How to avoid: Add field validation to your Firestore security rules using request.resource.data type checks, size limits, and the hasOnly() keys restriction.

Why it's a problem: Enabling App Check enforcement immediately without a monitoring period

How to avoid: Enable App Check in monitoring mode first. Review the App Check metrics dashboard for 7 days to confirm no legitimate requests are being blocked, then click Enforce.

Best practices

  • Never deploy with default Firestore test rules. Make rule deployment part of your pre-launch checklist.
  • Use Firebase's Blaze plan and set a spending alert at $5 and $20 — unexpected cost spikes are often the first sign of unauthorized API usage.
  • Enable Firebase Authentication email enumeration protection so attackers cannot discover which email addresses are registered.
  • Use the hasOnly() rule check to prevent users from injecting unexpected fields like isAdmin into their own documents.
  • Rotate API keys quarterly and revoke old keys immediately after any suspected breach.
  • Enable Firestore audit logging (Cloud Audit Logs) in production to maintain a record of all data access for compliance.
  • Test your Firestore rules using the Firebase emulator suite locally before deploying — a rules mistake can lock your own app out of its data.
  • Set up Firebase Security Rules unit tests using the @firebase/rules-unit-testing package so rule changes are caught before deployment.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

My FlutterFlow app uses Firebase. Review this Firestore security checklist and explain which items are most critical to implement before launch: secure rules, API key management, App Check, input validation, and rate limiting.

FlutterFlow Prompt

Update my FlutterFlow app's Firestore security rules to restrict access so users can only read and write their own documents. Add field validation to prevent oversized strings and unauthorized field injection. Show me the complete rules file.

Frequently asked questions

How do I know if my Firestore rules are currently open to the public?

Open the Firebase console, go to Firestore Database, and click the Rules tab. If you see 'allow read, write: if true;' anywhere without an authentication condition, your database is publicly accessible. Fix this immediately.

Can someone extract my API keys from my FlutterFlow app?

Yes. FlutterFlow compiles to Dart (mobile) and JavaScript (web). Both can be decompiled or inspected in browser developer tools. Any API key stored in the app code, App State, or widget properties is visible to anyone who looks. Always proxy sensitive API calls through Firebase Cloud Functions.

What is Firebase App Check and do I need it?

App Check verifies that requests to Firebase come from your actual app, not from scripts or unauthorized clients. It is not mandatory but strongly recommended for any production app. Without it, anyone who finds your Firebase config can call your Cloud Functions and query Firestore directly.

Does FlutterFlow's form validation protect my database?

No. Form validation in FlutterFlow only runs in the app UI. An attacker can skip the app entirely and send API requests directly to Firestore. You must add validation in your Firestore security rules using request.resource.data checks.

How do I set an admin role for specific users in Firebase?

Use Firebase Admin SDK in a Cloud Function to set a custom claim: auth.setCustomUserClaims(uid, { admin: true }). The user must sign out and back in for the claim to appear in their ID token. Reference it in Firestore rules with request.auth.token.admin == true.

Will stricter Firestore rules break my FlutterFlow app?

They might if your app queries data without passing the user's authentication token. Test rule changes in the Firebase Rules Playground first and use the FlutterFlow preview mode to verify all screens still load correctly after tightening rules.

How much does Firebase App Check cost?

Firebase App Check is free. Play Integrity (Android) and App Attest (iOS) attestation providers have no direct cost from Firebase. reCAPTCHA Enterprise for web has a free tier of 10,000 assessments per month.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.