Cursor + React + Firebase Security Blueprint

Share

To secure a Cursor + React + Firebase stack, you need to: (1) replace test-mode Firestore rules with production rules that check authentication and ownership, (2) configure Firebase Storage rules to restrict file access, (3) use React Context to manage auth state consistently across components, (4) understand that client-side route protection is for UX only - Firestore rules are the real security, and (5) create a .cursorignore file to prevent AI from accessing environment files. This blueprint covers Firestore rules, React auth patterns, and the security boundary between client and server.

Setup Time2-3 hours

TL;DR

React + Firebase is a popular stack for rapid prototyping, but AI-generated code often ships with insecure defaults. Key issues: test-mode Firestore rules that allow public access, Firebase config exposed without proper security rules, and missing auth state checks in protected components. Always configure security rules before deploying, use React Context for auth state, and never trust client-side route protection alone.

Platform Guides & Checklists

      Cursor Security Guide



      React Security Guide



      Firebase Security Guide



      Pre-Launch Checklist

Stack Security Overview

This stack relies heavily on client-side code, which means security rules in Firebase are your primary defense:

ComponentSecurity RoleCommon AI Mistakes
CursorCode generationGenerates permissive rules, skips auth checks
ReactUI layerClient-only route guards, exposed state
FirebaseBackend + AuthTest mode rules, missing validation

Part 1: Firestore Security Rules Firebase

The AI-Generated Problem Cursor Firebase

Cursor often generates code that works with test-mode rules:

DANGEROUS: Default test mode rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;  // Anyone can do anything!
    }
  }
}

Secure Rules Pattern Firebase

firestore.rules - Secure configuration
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // User profiles - only owner can read/write
    match /users/{userId} {
      allow read, update, delete: if request.auth != null
        && request.auth.uid == userId;
      allow create: if request.auth != null;
    }

    // Posts - public read, authenticated create, owner modify
    match /posts/{postId} {
      allow read: if true;
      allow create: if request.auth != null
        && request.resource.data.authorId == request.auth.uid;
      allow update, delete: if request.auth != null
        && resource.data.authorId == request.auth.uid;
    }

    // Private collections - strict user isolation
    match /private/{userId}/{document=**} {
      allow read, write: if request.auth != null
        && request.auth.uid == userId;
    }
  }
}

Part 2: React Auth Context React Firebase

Proper Auth State Management React

AI-generated React code often checks auth inconsistently. Use a centralized context:

src/contexts/AuthContext.tsx
import { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from '../lib/firebase';

interface AuthContextType {
  user: User | null;
  loading: boolean;
}

const AuthContext = createContext<AuthContextType>({
  user: null,
  loading: true
});

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
      setLoading(false);
    });
    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={{ user, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

Protected Route Component React

src/components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { user, loading } = useAuth();

  if (loading) {
    return <div>Loading...</div>;
  }

  if (!user) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

Remember: Client-side route protection is for UX only. Your Firestore security rules are the actual security barrier. Even with protected routes, malicious users can call Firebase directly.

Part 3: Firebase Configuration Firebase

Environment Variables React

.env (Create React App)
# These are safe to expose (security comes from rules)
REACT_APP_FIREBASE_API_KEY=AIza...
REACT_APP_FIREBASE_AUTH_DOMAIN=yourapp.firebaseapp.com
REACT_APP_FIREBASE_PROJECT_ID=yourapp
REACT_APP_FIREBASE_STORAGE_BUCKET=yourapp.appspot.com
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=123456789
REACT_APP_FIREBASE_APP_ID=1:123456789:web:abc123
src/lib/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);

Security Checklist

Pre-Launch Checklist for Cursor + React + Firebase

Firestore rules updated from test mode

Storage rules configured (if using)

Auth context provides consistent state

Protected routes use auth context

Firebase config uses environment variables

.cursorignore excludes .env files

Rules tested with Firebase Emulator

Auth domain configured for production URL

Alternative Stack Configurations

Cursor + Firebase + Vercel Same Firebase security with Vercel hosting. Adds server-side capabilities with API routes.

      Cursor + Supabase + Vercel
      Swap Firebase for Supabase. Different security model with PostgreSQL RLS instead of Firestore rules.


      Bolt.new + React + Firebase
      Same React + Firebase stack with Bolt.new. Different AI code generation approach.

Is it safe to expose Firebase config in React?

Yes, the Firebase client config (apiKey, authDomain, etc.) is designed for public exposure. Your security comes from Firestore and Storage rules, not from hiding these values. The apiKey identifies your project but doesn't grant access.

Why do I need both route protection and Firestore rules?

Route protection improves UX by redirecting unauthenticated users. But React runs on the client, so users can bypass it. Firestore rules run on Firebase's servers and are the actual security enforcement.

How do I test my security rules?

Use the Firebase Emulator Suite locally or the Rules Playground in Firebase Console. Write test cases for authenticated users, unauthenticated users, and users trying to access other users' data.

Building React + Firebase with Cursor?

Scan your app for security rule issues and auth problems.

Start Free Scan
Security Blueprints

Cursor + React + Firebase Security Blueprint