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

How to Manage Large-Scale User Authentication with Firebase in FlutterFlow

Scaling Firebase Authentication in FlutterFlow beyond 10,000 users requires four additions to the default setup: custom claims for role-based access control (so Firestore rules can enforce permissions without querying a users collection), Firebase App Check to prevent automated abuse, structured session management via Firestore, and account linking to handle users who sign up with multiple providers. Never create a separate Firebase project per tenant — use custom claims for multi-tenancy.

What you'll learn

  • How to set and use Firebase custom claims for role-based access control at scale
  • How to implement Firebase App Check to protect against credential stuffing and bots
  • How to manage user sessions and force token refresh from a Cloud Function
  • How to handle account linking when a user tries to sign up with an already-linked provider
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate11 min read60-90 minFlutterFlow Free+ for UI; Firebase Blaze plan required for Cloud Functions and App Check enforcementMarch 2026RapidDev Engineering Team
TL;DR

Scaling Firebase Authentication in FlutterFlow beyond 10,000 users requires four additions to the default setup: custom claims for role-based access control (so Firestore rules can enforce permissions without querying a users collection), Firebase App Check to prevent automated abuse, structured session management via Firestore, and account linking to handle users who sign up with multiple providers. Never create a separate Firebase project per tenant — use custom claims for multi-tenancy.

What Changes at 10,000+ Users

The default FlutterFlow Firebase Auth setup (email/password + Google sign-in, Firestore user document) works well up to a few thousand users. At scale, several problems emerge. Firestore security rules that query the users collection on every read request become a bottleneck — these queries count against your read quota and add latency. Rate limits on Firebase Auth operations become relevant: 1,000 creates per second, 3,000 sign-ins per second. User management operations (bulk password resets, disabling accounts, auditing) become painful through the Firebase Console alone. And credential stuffing attacks — automated scripts trying leaked email/password combinations — can exhaust your Auth quota. Each of these problems has a specific architectural solution that plugs into FlutterFlow without requiring a full platform change.

Prerequisites

  • FlutterFlow project with Firebase Authentication enabled and at least one auth provider configured
  • Firebase Blaze plan (required for Cloud Functions and App Check)
  • Familiarity with Firestore security rules syntax
  • A users Firestore collection with UID as the document ID

Step-by-step guide

1

Add custom claims for role-based access control

Custom claims are key-value pairs embedded in a user's Firebase ID token. They are set server-side via the Admin SDK and are available in both your app and Firestore security rules without any additional database read. Create a Cloud Function called setUserRole that accepts a UID and a role string, and uses admin.auth().setCustomUserClaims(uid, { role }) to set the claim. Call this function from your admin panel whenever you promote a user. In your Firestore security rules, check request.auth.token.role instead of querying your users collection: allow read: if request.auth.token.role == 'admin'. In FlutterFlow, force a token refresh after role changes using IdToken refresh so the new claim takes effect immediately.

functions/setUserRole.js
1// functions/setUserRole.js
2const { onCall } = require('firebase-functions/v2/https');
3const { getAuth } = require('firebase-admin/auth');
4const { initializeApp } = require('firebase-admin/app');
5
6initializeApp();
7
8const VALID_ROLES = ['user', 'moderator', 'admin', 'enterprise'];
9
10exports.setUserRole = onCall(async (request) => {
11 // Only admins can set roles
12 if (request.auth?.token?.role !== 'admin') {
13 throw new Error('Unauthorized');
14 }
15 const { targetUid, role } = request.data;
16 if (!VALID_ROLES.includes(role)) {
17 throw new Error(`Invalid role: ${role}`);
18 }
19 await getAuth().setCustomUserClaims(targetUid, { role });
20 return { success: true, uid: targetUid, role };
21});

Expected result: Users have a role claim in their ID token. Firestore rules enforce access based on the claim without querying the users collection.

2

Enable Firebase App Check to block automated abuse

App Check verifies that requests come from your legitimate app, not from automated scripts or emulators. In Firebase Console > App Check, register your app with the appropriate attestation provider: Play Integrity (Android), DeviceCheck or App Attest (iOS). Enable App Check enforcement for Firebase Authentication. In FlutterFlow add the firebase_app_check package to your custom code dependencies and initialize it with your platform's provider in your app's startup Custom Action. Once enforced, sign-in requests from emulators, scripts, or unofficial clients are rejected before they count against your quota. This alone can reduce credential stuffing attacks by over 95%.

initAppCheck.dart
1// Custom Action: initAppCheck.dart
2import 'package:firebase_app_check/firebase_app_check.dart';
3import 'package:flutter/foundation.dart';
4
5Future<void> initAppCheck() async {
6 await FirebaseAppCheck.instance.activate(
7 androidProvider: kDebugMode
8 ? AndroidProvider.debug
9 : AndroidProvider.playIntegrity,
10 appleProvider: kDebugMode
11 ? AppleProvider.debug
12 : AppleProvider.deviceCheck,
13 webProvider: ReCaptchaV3Provider('YOUR_SITE_KEY'),
14 );
15}

Expected result: Automated sign-in attempts from scripts and emulators are rejected by Firebase before reaching your Auth quota.

3

Implement structured session management in Firestore

At scale you need visibility into active sessions and the ability to revoke them server-side. Firebase Auth tokens expire after 1 hour and refresh automatically, but the refresh token itself does not expire until revoked. Create a sessions Firestore collection where each document has: userId, createdAt, lastActiveAt, deviceInfo, and isRevoked. Write a session document on login via a Cloud Function (or Custom Action). Update lastActiveAt on each app open. To force sign-out a user (e.g., on account compromise), call admin.auth().revokeRefreshTokens(uid) from a Cloud Function and set isRevoked: true on their session document. In FlutterFlow check the session document on app load and force sign-out if isRevoked is true.

functions/revokeUserSession.js
1// functions/revokeUserSession.js
2const { onCall } = require('firebase-functions/v2/https');
3const { getAuth } = require('firebase-admin/auth');
4const { getFirestore } = require('firebase-admin/firestore');
5
6exports.revokeUserSession = onCall(async (request) => {
7 if (request.auth?.token?.role !== 'admin') {
8 throw new Error('Unauthorized');
9 }
10 const { targetUid, reason } = request.data;
11 await getAuth().revokeRefreshTokens(targetUid);
12 // Mark all sessions as revoked
13 const db = getFirestore();
14 const sessionsSnap = await db.collection('sessions')
15 .where('userId', '==', targetUid)
16 .where('isRevoked', '==', false).get();
17 const batch = db.batch();
18 sessionsSnap.docs.forEach(doc => {
19 batch.update(doc.ref, {
20 isRevoked: true,
21 revokedAt: new Date(),
22 revokedReason: reason,
23 });
24 });
25 await batch.commit();
26 return { revoked: sessionsSnap.size };
27});

Expected result: Admin can force sign-out any user. The user is redirected to the login screen within seconds of revocation.

4

Handle account linking for users with multiple sign-in providers

At scale, a common issue is users who sign up with Google, then later try to sign in with email/password using the same email address — Firebase throws an 'account-exists-with-different-credential' error. Handle this gracefully by catching the error in a FlutterFlow Custom Action, fetching the sign-in methods for that email, and prompting the user to sign in with the existing provider first, then link the new credential. Use FirebaseAuth.instance.currentUser?.linkWithCredential() after the initial sign-in to attach the new provider to the existing account. Store linked providers in the user's Firestore document so your admin panel can show which sign-in methods each user has.

handleAccountLinking.dart
1// Custom Action: handleAccountLinking.dart
2import 'package:firebase_auth/firebase_auth.dart';
3
4Future<String> getSignInMethodsForEmail(String email) async {
5 try {
6 final methods = await FirebaseAuth.instance
7 .fetchSignInMethodsForEmail(email);
8 return methods.join(','); // e.g., 'google.com,password'
9 } catch (e) {
10 return 'error:$e';
11 }
12}
13
14Future<bool> linkEmailPasswordToCurrentUser(
15 String email, String password) async {
16 try {
17 final credential = EmailAuthProvider.credential(
18 email: email, password: password);
19 await FirebaseAuth.instance.currentUser
20 ?.linkWithCredential(credential);
21 return true;
22 } on FirebaseAuthException catch (e) {
23 // credential-already-in-use means another account has this email
24 return false;
25 }
26}

Expected result: Users who sign up with multiple providers have a single unified account. No duplicate user documents are created in Firestore.

5

Perform bulk user operations via the Admin SDK

At scale you need to do bulk operations: import 10,000 users from a migration, disable all users from a compromised batch, or export all user emails for a compliance audit. Firebase Console handles up to a few hundred users before becoming impractical. Create admin Cloud Functions for each operation. For bulk import use admin.auth().importUsers() with up to 1,000 users per call. For bulk updates query your users collection in batches and call setCustomUserClaims or updateUser for each. For export use admin.auth().listUsers() which returns 1,000 users per page — loop through pages and accumulate results. Expose these operations from a secure admin page in FlutterFlow accessible only to users with role == 'admin' in their custom claim.

functions/bulkUserExport.js
1// functions/bulkUserExport.js
2const { onCall } = require('firebase-functions/v2/https');
3const { getAuth } = require('firebase-admin/auth');
4
5exports.exportAllUsers = onCall(async (request) => {
6 if (request.auth?.token?.role !== 'admin') {
7 throw new Error('Unauthorized');
8 }
9 const users = [];
10 let pageToken;
11 do {
12 const result = await getAuth().listUsers(1000, pageToken);
13 result.users.forEach(user => {
14 users.push({
15 uid: user.uid,
16 email: user.email,
17 createdAt: user.metadata.creationTime,
18 lastSignIn: user.metadata.lastSignInTime,
19 disabled: user.disabled,
20 customClaims: user.customClaims,
21 });
22 });
23 pageToken = result.pageToken;
24 } while (pageToken);
25 return { count: users.length, users };
26});

Expected result: Admins can export all user data, perform bulk updates, and manage users at scale without using the Firebase Console.

Complete working example

functions/authManagement.js
1const { onCall } = require('firebase-functions/v2/https');
2const { getAuth } = require('firebase-admin/auth');
3const { getFirestore, FieldValue } = require('firebase-admin/firestore');
4const { initializeApp } = require('firebase-admin/app');
5
6initializeApp();
7
8const requireAdmin = (request) => {
9 if (request.auth?.token?.role !== 'admin') {
10 throw new Error('Unauthorized: admin role required');
11 }
12};
13
14exports.setUserRole = onCall(async (request) => {
15 requireAdmin(request);
16 const { targetUid, role } = request.data;
17 const validRoles = ['user', 'moderator', 'admin', 'enterprise'];
18 if (!validRoles.includes(role)) throw new Error('Invalid role');
19 await getAuth().setCustomUserClaims(targetUid, { role });
20 await getFirestore().collection('users').doc(targetUid)
21 .update({ role, roleUpdatedAt: FieldValue.serverTimestamp() });
22 return { success: true };
23});
24
25exports.revokeUserSession = onCall(async (request) => {
26 requireAdmin(request);
27 const { targetUid, reason } = request.data;
28 await getAuth().revokeRefreshTokens(targetUid);
29 const db = getFirestore();
30 const snap = await db.collection('sessions')
31 .where('userId', '==', targetUid)
32 .where('isRevoked', '==', false).get();
33 const batch = db.batch();
34 snap.docs.forEach(d => batch.update(d.ref, {
35 isRevoked: true,
36 revokedAt: FieldValue.serverTimestamp(),
37 revokedReason: reason || 'admin_action',
38 }));
39 await batch.commit();
40 return { revoked: snap.size };
41});
42
43exports.exportAllUsers = onCall(async (request) => {
44 requireAdmin(request);
45 const users = [];
46 let pageToken;
47 do {
48 const result = await getAuth().listUsers(1000, pageToken);
49 result.users.forEach(u => users.push({
50 uid: u.uid, email: u.email,
51 createdAt: u.metadata.creationTime,
52 lastSignIn: u.metadata.lastSignInTime,
53 disabled: u.disabled, role: u.customClaims?.role || 'user',
54 }));
55 pageToken = result.pageToken;
56 } while (pageToken);
57 return { count: users.length, users };
58});

Common mistakes

Why it's a problem: Creating a separate Firebase project per client or tenant for multi-tenancy

How to avoid: Use custom claims for multi-tenancy. Add an orgId claim to each user's token. Scope all Firestore queries and security rules by orgId. Use Firebase Auth multi-tenancy (Identity Platform) for full tenant isolation if required by compliance.

Why it's a problem: Checking user roles by querying the Firestore users collection in security rules

How to avoid: Use custom claims in Firebase ID tokens. Claims are embedded in the JWT and available in security rules as request.auth.token.role without any database read.

Why it's a problem: Not forcing ID token refresh after updating custom claims

How to avoid: After calling setCustomUserClaims, signal the client to refresh its token. In FlutterFlow add a Custom Action that calls FirebaseAuth.instance.currentUser?.getIdToken(true) to force a fresh token.

Best practices

  • Use Firebase Auth's built-in rate limiting as a first line of defense, then add App Check for application-level attestation.
  • Store custom claim values that are checked frequently (role, planTier) in claims, and detailed profile data in Firestore — never put large objects in claims.
  • Run a weekly Cloud Function that audits users with elevated roles (admin, enterprise) and sends a report to your security contact.
  • Implement IP-based suspicious login detection: if a user signs in from a new country within 24 hours of a previous sign-in, log the event and optionally require re-authentication.
  • Use Firebase Auth email enumeration protection (enabled in Firebase Console > Auth > Settings) to prevent attackers from discovering registered email addresses.
  • Test your Firestore security rules with the Firebase Rules Playground after every rule change — a misconfigured rule can silently block your own users.
  • Keep an audit log of all admin operations (role changes, session revocations, bulk imports) in a Firestore admin_audit collection for compliance and debugging.

Still stuck?

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

ChatGPT Prompt

I have a FlutterFlow app with Firebase Authentication and it is approaching 10,000 users. What changes do I need to make to scale safely? Specifically: how do custom claims work for role-based access control, how does Firebase App Check prevent abuse, and what is the best way to manage user sessions and force sign-out from the server side?

FlutterFlow Prompt

Write a set of Firebase Cloud Functions in Node.js for large-scale user management: one to set a custom role claim on a user, one to revoke all active sessions for a user, and one to export all users with pagination. All functions should require an admin custom claim to call.

Frequently asked questions

What is the maximum number of users Firebase Authentication supports?

Firebase Authentication does not have a published hard user limit. It is designed to scale to hundreds of millions of users. The limits that matter at scale are operational: 1,000 user creations per second and 3,000 sign-ins per second per project. For typical FlutterFlow apps these limits are not a concern.

How long does it take for custom claims to take effect after being set?

Custom claims are set immediately in Firebase's backend, but the user's current ID token will not contain the new claims until it is refreshed. ID tokens are refreshed automatically after 1 hour. For immediate effect, call FirebaseAuth.instance.currentUser?.getIdToken(true) to force a refresh.

Can I have multiple roles in custom claims?

Yes. Custom claims can be any JSON object within the 1000-byte limit. You can use an array of roles: { roles: ['admin', 'moderator'] } and check request.auth.token.roles.hasAny(['admin']) in Firestore rules. However, simpler is usually better — a single role string is easier to reason about and debug.

What does Firebase App Check protect against?

App Check verifies that API calls come from your legitimate app binary on a real device. It protects against: credential stuffing (automated scripts testing leaked passwords), unauthorized API access from non-app clients, and scraping of your Firebase data. It does not protect against compromised legitimate devices.

How do I handle users who delete their Firebase Auth account but still have Firestore data?

Use a Firebase Auth onDelete Cloud Function trigger: functions.auth.user().onDelete(). When a user deletes their account, the function automatically fires and can clean up their Firestore documents, Storage files, and any other associated data. Set this up from the start — orphaned data accumulates quickly at scale.

Can RapidDev help with migrating a large user base to Firebase?

Yes. RapidDev has migrated large user bases (10,000+ accounts) to Firebase Auth using the Admin SDK's importUsers function with password hash preservation. We handle the migration planning, hash format conversion, and validation testing. Contact us if you are moving from Auth0, Cognito, or a custom auth system.

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.