Security monitoring for FlutterFlow apps requires multiple layers: Firebase App Check for client integrity, Cloud Logging for Firestore Rules violations, custom audit logs for sensitive operations, and Cloud Functions with threshold-based alerts sent via email or Slack. The critical mistake is manual monthly checks — a breach or credential stuffing attack can drain your database or generate thousands in Firebase costs within minutes.
Security Monitoring You Will Actually Act On
Most FlutterFlow developers set up Firestore Security Rules and call it done. But rules only tell Firebase what to deny — they do not tell you when attacks are happening, how bad they are, or when your app's security configuration degrades over time. This guide builds an active monitoring layer: App Check metrics to detect unauthorized API clients, auth failure tracking to spot credential stuffing, a custom audit log for sensitive data access, and automated alerts so you know about incidents in minutes rather than months.
Prerequisites
- Firebase Blaze (pay-as-you-go) plan for Cloud Functions and App Check
- Firebase project with Firestore, Authentication, and App Check configured
- Basic Cloud Functions knowledge (Node.js) for alert automation
- A notification channel: email via SendGrid or Slack webhook for alerts
Step-by-step guide
Enable and Monitor Firebase App Check
Enable and Monitor Firebase App Check
Firebase App Check verifies that API requests come from your legitimate app binary, not from unauthorized scripts or reverse-engineered clients. In Firebase Console, go to App Check > Register your app. Use Play Integrity for Android, DeviceCheck for iOS, and reCAPTCHA Enterprise for web. After registering, enable Enforcement mode in App Check settings — this starts blocking unregistered clients from accessing Firebase services. Monitor the App Check Metrics dashboard to see the ratio of verified vs unverified requests. A sudden spike in unverified requests means someone is trying to use your Firebase project directly without your app.
Expected result: Firebase Console > App Check > Metrics shows your legitimate app requests as 'Verified' and reveals if any unregistered clients are making requests to your project.
Track Authentication Failures with Cloud Logging
Track Authentication Failures with Cloud Logging
Firebase Authentication writes all auth events to Google Cloud Logging. Navigate to Google Cloud Console > Logging > Logs Explorer. Filter by resource.type='identitytoolkit.googleapis.com' and look for SIGN_IN_FAILURE, USER_NOT_FOUND, and WRONG_PASSWORD events. Create a log-based metric that counts authentication failures per IP address per minute. In Cloud Monitoring, create an alerting policy that fires when this metric exceeds a threshold — for example, 10 failed logins from the same IP within 5 minutes indicates a credential stuffing or brute force attack. Route alerts to your email or a PagerDuty integration.
1// Cloud Function: triggered by Auth log sink via Pub/Sub2// Creates alert when > 10 failed logins from same IP in 5 minutes34const functions = require('firebase-functions');5const admin = require('firebase-admin');6const axios = require('axios');78exports.monitorAuthFailures = functions.pubsub9 .topic('auth-events')10 .onPublish(async (message) => {11 const event = JSON.parse(Buffer.from(message.data, 'base64').toString());12 if (event.event_type !== 'SIGN_IN_FAILURE') return;1314 const ip = event.ip_address;15 const db = admin.firestore();16 const now = Date.now();17 const windowStart = now - 5 * 60 * 1000; // 5 minutes1819 // Track failure count for this IP in the last 5 minutes20 const ref = db.collection('security_events').doc(`ip_${ip.replace(/\./g, '_')}`);21 await db.runTransaction(async (tx) => {22 const snap = await tx.get(ref);23 const data = snap.data() || { failures: [], alerted: false };2425 // Remove entries older than 5 minutes26 const recent = (data.failures || []).filter((t) => t > windowStart);27 recent.push(now);2829 if (recent.length >= 10 && !data.alerted) {30 await sendSecurityAlert(`Possible brute force attack from IP: ${ip}. ${recent.length} failed logins in 5 minutes.`);31 tx.set(ref, { failures: recent, alerted: true, alerted_at: now });32 } else {33 tx.set(ref, { failures: recent, alerted: data.alerted });34 }35 });36 });3738async function sendSecurityAlert(message) {39 const slackWebhook = process.env.SLACK_SECURITY_WEBHOOK;40 if (slackWebhook) {41 await axios.post(slackWebhook, { text: `🚨 Security Alert: ${message}` });42 }43}Expected result: When 10+ failed login attempts occur from the same IP within 5 minutes, a Slack alert fires with the IP address, timestamp, and failure count.
Build a Custom Audit Log for Sensitive Operations
Build a Custom Audit Log for Sensitive Operations
Firestore Security Rules log denials in Cloud Logging, but successful reads and writes of sensitive data are not automatically audited. Build a custom audit log by creating a Firestore collection called 'audit_log' and writing an entry for every sensitive operation: reading financial records, updating subscription status, accessing PII, or changing user roles. Create a Cloud Function triggered on writes to sensitive collections that appends to the audit log. Each audit entry should contain: user_id, operation (read/write/delete), collection, document_id, timestamp, ip_address (from request context), and a summary of changed fields.
1// Cloud Function: audit log writer for sensitive collections2exports.auditSensitiveWrites = functions.firestore3 .document('subscriptions/{subId}')4 .onWrite(async (change, context) => {5 const before = change.before.data() || {};6 const after = change.after.data() || {};7 const userId = context.auth?.uid;89 // Build diff of changed fields10 const changedFields = Object.keys(after).filter(11 (k) => JSON.stringify(before[k]) !== JSON.stringify(after[k])12 );1314 await admin.firestore().collection('audit_log').add({15 user_id: userId || 'unauthenticated',16 operation: !change.before.exists ? 'create'17 : !change.after.exists ? 'delete' : 'update',18 collection: 'subscriptions',19 document_id: context.params.subId,20 changed_fields: changedFields,21 timestamp: admin.firestore.FieldValue.serverTimestamp(),22 event_id: context.eventId,23 });24 });Expected result: Every write to the subscriptions collection creates a corresponding audit_log entry with the user ID, operation type, and list of changed fields.
Monitor Firestore Security Rules Violations
Monitor Firestore Security Rules Violations
When Firestore Security Rules deny a request, Firebase writes a log entry to Cloud Logging with the resource path and the rule that blocked it. In Google Cloud Console > Logging > Logs Explorer, filter for firestore.googleapis.com and PERMISSION_DENIED. Create a log-based metric counting denials by resource path. High denial rates on a specific document path mean either your rules are too strict (legitimate users blocked) or an attacker is probing your data structure. In Cloud Monitoring, set an alert for more than 50 rule denials per minute on any single path.
Expected result: Cloud Monitoring dashboard shows a real-time graph of Firestore rule denials per minute, with an alert that fires when a path receives an unusual spike in denied requests.
Set Up a Security Dashboard in Your FlutterFlow Admin Panel
Set Up a Security Dashboard in Your FlutterFlow Admin Panel
Create an AdminSecurity page in FlutterFlow visible only to users with the 'admin' role in Firestore. Add widgets to display: recent audit_log entries as a ListView (last 50 entries, ordered by timestamp descending), a count of security_events documents with alerts flagged in the last 24 hours, the App Check verification ratio from a Firestore document that your Cloud Function writes daily, and a list of the top 5 most-denied Firestore paths from the last hour. This gives RapidDev and your team a single pane of glass for security health without navigating multiple Firebase console tabs.
Expected result: Admins can view real-time security metrics and recent suspicious events in a single FlutterFlow page without leaving the app.
Complete working example
1// Firebase Cloud Functions: security monitoring suite2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const axios = require('axios');56if (!admin.apps.length) admin.initializeApp();78const db = admin.firestore();9const SLACK_WEBHOOK = process.env.SLACK_SECURITY_WEBHOOK;10const ALERT_THRESHOLD_FAILURES = 10;11const ALERT_WINDOW_MS = 5 * 60 * 1000;1213// ─── Auth Failure Monitor ────────────────────────────────────────────────────14exports.monitorAuthFailures = functions.pubsub15 .topic('firebase-auth-events')16 .onPublish(async (msg) => {17 let event;18 try {19 event = JSON.parse(Buffer.from(msg.data, 'base64').toString());20 } catch { return; }2122 if (!['SIGN_IN_FAILURE', 'USER_NOT_FOUND'].includes(event.event_type)) return;2324 const ip = event.ip_address || 'unknown';25 const key = `ip_${ip.replace(/[.:]/g, '_')}`;26 const ref = db.collection('security_events').doc(key);27 const now = Date.now();2829 await db.runTransaction(async (tx) => {30 const snap = await tx.get(ref);31 const data = snap.exists ? snap.data() : { failures: [], alerted_at: 0 };32 const recent = (data.failures || []).filter((t) => t > now - ALERT_WINDOW_MS);33 recent.push(now);34 const cooldownOver = now - (data.alerted_at || 0) > 30 * 60 * 1000;3536 if (recent.length >= ALERT_THRESHOLD_FAILURES && cooldownOver) {37 await notify(`Brute force suspected from IP ${ip}: ${recent.length} failures in 5 min`);38 tx.set(ref, { failures: recent, alerted_at: now, ip }, { merge: true });39 } else {40 tx.set(ref, { failures: recent }, { merge: true });41 }42 });43 });4445// ─── Daily Security Summary ───────────────────────────────────────────────────46exports.dailySecuritySummary = functions.pubsub47 .schedule('0 8 * * *')48 .timeZone('UTC')49 .onRun(async () => {50 const yesterday = new Date(Date.now() - 86400000);51 const eventsSnap = await db.collection('security_events')52 .where('alerted_at', '>', yesterday.getTime())53 .get();5455 const auditSnap = await db.collection('audit_log')56 .where('timestamp', '>', admin.firestore.Timestamp.fromDate(yesterday))57 .get();5859 await notify(60 `Daily Security Summary:\n` +61 `• Security events with alerts: ${eventsSnap.size}\n` +62 `• Audit log entries: ${auditSnap.size}`63 );6465 // Write summary to Firestore for admin dashboard66 await db.collection('security_metrics').add({67 date: admin.firestore.Timestamp.now(),68 alert_count: eventsSnap.size,69 audit_count: auditSnap.size,70 });71 });7273async function notify(message) {74 if (!SLACK_WEBHOOK) return;75 await axios.post(SLACK_WEBHOOK, { text: message }).catch(() => {});76}Common mistakes when monitoring Your FlutterFlow App's Security
Why it's a problem: Only checking security metrics manually once a month
How to avoid: Set up automated alerts with a response time under 5 minutes. Use Cloud Monitoring alerting policies and Slack/email notifications so you are alerted immediately when thresholds are crossed, regardless of what time it is.
Why it's a problem: Relying only on Firestore Security Rules without App Check
How to avoid: Enable Firebase App Check in enforcement mode. This verifies that every request comes from your legitimate app binary before it even reaches your Security Rules.
Why it's a problem: Writing audit logs with the client app instead of a Cloud Function
How to avoid: Always write audit log entries from Cloud Functions triggered by Firestore onCreate/onUpdate/onDelete events. The Cloud Function runs server-side and cannot be bypassed by a client.
Best practices
- Enable App Check in Monitor mode first, confirm >99% of traffic is verified for 48 hours, then switch to Enforce mode to avoid blocking legitimate users.
- Set Cloud Monitoring alert fatigue thresholds carefully — an alert that fires 100 times a day trains your team to ignore it. Start conservative and tune thresholds based on your app's normal traffic patterns.
- Store security alert notifications in a Firestore security_alerts collection in addition to sending Slack messages — this creates a searchable history even if your Slack workspace loses the messages.
- Rotate your Firebase service account keys every 90 days and invalidate old keys in Google Cloud Console > IAM > Service Accounts.
- Review Firestore Security Rules monthly using the Firebase Rules Playground with real-world attack scenarios: unauthenticated access, accessing another user's documents, and malformed data writes.
- For apps handling payments or PII, have RapidDev or a security consultant review your complete Firestore Security Rules before launch.
- Set up a Firebase budget alert in Google Cloud Console Billing to detect unexpected cost spikes that could indicate unauthorized data access or API abuse.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app with Firebase. Explain how to set up comprehensive security monitoring including: Firebase App Check enforcement, Cloud Logging alerts for authentication failures and brute force detection, custom Firestore audit logs for sensitive operations written from Cloud Functions, and a Slack alerting Cloud Function with cooldown to prevent alert fatigue. Show Node.js Cloud Function code.
In my FlutterFlow app, create an AdminSecurity page that is only accessible to users with admin role in Firestore. The page should show a ListView of the last 50 audit_log Firestore documents ordered by timestamp descending, a Text widget showing the count of security_events where alerted_at is not null in the last 24 hours, and a button to navigate to Firebase Console.
Frequently asked questions
Does Firebase App Check completely prevent API abuse?
App Check significantly raises the bar — it blocks simple scripts and unauthorized clients. However, a sophisticated attacker can extract the App Check token from your app binary and replay it. App Check is a deterrent, not an absolute barrier. Combine it with rate limiting, authentication requirements, and anomaly detection for defense in depth.
How much does security monitoring cost on Firebase?
Cloud Logging and Cloud Monitoring have generous free tiers: 50GB of log storage per month and 1 alerting policy free. Cloud Functions for alert processing cost cents per month for typical alert volumes. The main cost driver is Firestore reads/writes for the audit log — for a small app with 100 sensitive operations per day, this is well under the free Spark plan limits.
What should I do when I receive a security alert?
First, verify if it is a false positive by checking the IP address against known bot scrapers. Second, if it appears genuine, temporarily block the offending IP using Cloud Armor or a Cloud Function middleware. Third, check your audit log for what data was accessed. Fourth, review your Firestore Security Rules for any misconfiguration. Fifth, notify affected users if personal data was potentially accessed, as required by GDPR and similar regulations.
Can I see who read my Firestore documents, not just who wrote to them?
Firestore does not log reads in Cloud Logging by default — only writes and denials. To audit reads of sensitive documents, write a Cloud Function triggered by a client-side Custom Action that explicitly logs 'I am reading document X' to your audit_log collection. This is a self-reported audit and less reliable than write-triggered logs, but it is the only available approach without switching to a different database.
How do I monitor for unusual data export patterns?
Create a Cloud Function that tracks the number of Firestore read operations per user per hour using a rate counter in a separate 'usage_metrics' collection. Alert when a single user reads more than a configured threshold of documents in a short period. This catches data scraping behavior where someone is systematically downloading all records they have access to.
Should I store security audit logs in Firestore or a separate system?
For most FlutterFlow apps, Firestore is fine for audit logs. The limitation is that a compromised admin account could theoretically delete audit logs. For higher-security requirements (healthcare, finance), write audit logs to Cloud Storage as append-only files using Cloud Functions, which cannot be deleted via normal Firestore client operations. This provides a tamper-resistant audit trail.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation