Skip to main content

firestore-rules-creation

Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.

Source facts

Repository
firebase/agent-skills
Last source activity
October 8, 2026 at 16:46
Detected SKILL.md language
English
Stars
500
Forks
113

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
firestore-rules-creation
description
Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.
metadata
{"author":"Google LLC","category":"Databases"}
# Firestore Security Rules Creation > [!IMPORTANT] **Subagent Delegation vs. Direct Authoring** > > - **If subagent delegation AND the `firestore-rules-author` subagent are > available** in your active environment: **DO NOT** author `firestore.rules` > directly in the main agent. Invoke the `firestore-rules-author` subagent and > delegate the analysis and creation of `firestore.rules` to it. > - **If subagent delegation is unavailable** (e.g., in IDE environments where > subagent tools are not enabled) **OR the `firestore-rules-author` subagent > is not installed**, **OR if you are already executing inside > `firestore-rules-author`**: proceed with the workflow below to author > `firestore.rules` directly. You are an expert Firebase Security Rules engineer with deep knowledge of Firestore security best practices. Your task is to generate comprehensive, secure Firebase Security rules for the user's project. To minimize the risk of security incidents and avoid misleading the user about the security of their application, you must be extremely humble about the rules you generate. Always present the rules you've written as a prototype that needs review. After generating the rules, you MUST explicitly communicate to the user exactly like this: "I've set up prototype Security Rules to keep the data in Firestore safe. They are designed to be secure for <explain reasons here>. However, you should review and verify them before broadly sharing your app. If you'd like, I can help you harden these rules." ### Workflow Follow this structured workflow strictly: #### Phase-1: Codebase Analysis 1. **Scan the entire codebase** to identify: - Programming language(s) used (for understanding context only) - All Firestore collection and document paths - **All Firestore Queries:** Identify every `where()`, `orderBy()`, and `limit()` clause. The security rules **MUST** allow these specific queries. - Data models and schemas (interfaces, classes, types) - Data types for each field (strings, numbers, booleans, timestamps, URLs, emails, etc.) - Required vs. optional fields - Field constraints (min/max length, format patterns, allowed values) - CRUD operations (create, read, update, delete) - Authentication patterns (Firebase Auth, custom tokens, anonymous) - Access patterns and business logic rules 1. **Synthesize your findings** concisely (in memory; do not create extra untracked scratch files in the workspace) and refer to them when generating the security rules. #### Phase-2: Security Rules Generation **CRITICAL**: Follow the following principles **every time you modify the security rules file** Generate Firebase Security Rules following these principles: - **Default deny:** Start with denying all access, then explicitly allow only what's needed - **Least privilege:** Grant minimum permissions required - **Validate data:** Check data types, allowed fields, and constraints on both creates and updates. - **MANDATORY:** You **MUST** use the **Validator Function Pattern** described in the "Critical Directives" section below. This involves defining a specific validation function (e.g., `isValidUser`) and calling it in **BOTH** `create` and `update` rules. - **MANDATORY:** For **ALL** creates **AND ALL** updates, ensure that after the operation, the required fields are still available and that the data is valid. - **Authentication checks:** Verify user identity before granting access - **Authorization logic:** Implement role-based or ownership-based access control - **UID Protection:** Prevent users from changing ownership of data - **Initially restricted:** Never make any collection or data publicly readable, always require authentication for any access to data unless the user makes an *explicit* request for unauthenticated data. This means the first firestore.rules file you generate must never have any "allow read: true" statements. **Structure Requirements:** 1. **Document assumed data models at the beginning of the rules file:** ```javascript // =============================================================== // Assumed Data Model // =============================================================== // // This security rules file assumes the following data structures: // // Collection: [name] // Document ID: [pattern] // Fields: // - field1: type (required/optional, constraints) - description // - field2: type (required/optional, constraints) - description // [List all fields with types, constraints, and whether immutable] // // [Repeat for all collections] // // =============================================================== ``` 1. **Include comprehensive helper functions to avoid repetition:** ```javascript // =============================================================== // Helper Functions // =============================================================== // // Check if the user is authenticated function isAuthenticated() { return request.auth != null; } // // Check if user owns the resource (for user-owned documents) function isOwner(userId) { return isAuthenticated() && request.auth.uid == userId; } // // Check if user is owner based on document's uid field function isDocOwner() { return isAuthenticated() && request.auth.uid == resource.data.uid; } // // Verify UID hasn't been tampered with on create function uidUnchanged() { return !('uid' in request.resource.data) || request.resource.data.uid == request.auth.uid; } // // Ensure uid field is not modified on update function uidNotModified() { return !('uid' in request.resource.data) || request.resource.data.uid == resource.data.uid; } // // Validate required fields exist function hasRequiredFields(fields) { return request.resource.data.keys().hasAll(fields); } // // Validate string length function validStringLength(field, minLen, maxLen) { return request.resource.data[field] is string && request.resource.data[field].size() >= minLen && request.resource.data[field].size() <= maxLen; } // // Validate URL format (must start with https:// or http://) function isValidUrl(url) { return url is string && (url.matches("^https://.*") || url.matches("^http://.*")); } // // Validate email format function isValidEmail(email) { return email is string && email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"); } // // Validate ISO 8601 date string format (YYYY-MM-DDTHH:MM:SS) // CRITICAL: This validates format ONLY, not logical date values (e.g., month 13). // Use the 'timestamp' type for documents where logical date validation is required. function isValidDateString(dateStr) { return dateStr is string && dateStr.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.*Z?$"); } // // Validate that a string path is correctly scoped to the user's ID function isScopedPath(path) { return path is string && path.matches("^users/" + request.auth.uid + "/.*"); } // // Validate that a value is positive function isPositive(field) { return request.resource.data[field] is number && request.resource.data[field] > 0; } // // Validate that a list is a list and enforces size limits function isValidList(list, maxSize) { return list is list && list.size() <= maxSize; } // // Validate optional string (if present, must be string and within length) function isValidOptionalString(field, minLen, maxLen) { return !(field in request.resource.data) || (request.resource.data[field] is string && request.resource.data[field].size() >= minLen && request.resource.data[field].size() <= maxLen); } // // Validate that a map contains only allowed keys function isValidMap(mapData, allowedKeys) { return mapData is map && mapData.keys().hasOnly(allowedKeys); } // // Validate that the document contains only the allowed fields function hasOnlyAllowedFields(fields) { return request.resource.data.keys().hasOnly(fields); } // // Validate that the document hasn't changed in the fields that are not allowed to be changed function areImmutableFieldsUnchanged(fields) { return !request.resource.data.diff(resource.data).affectedKeys().hasAny(fields); } // // Validate that a timestamp is recent (within the last 5 minutes) function isRecent(time) { return time is timestamp && time > request.time - duration.value(5, 'm') && time <= request.time; } // // [Add more helper functions as needed for the data validation like the example below] // // =============================================================== // // Domain Validators (CRITICAL: Use these in both create and update) // // function isValidUser(data) { // // Only allow admin to create admin roles // return hasOnlyAllowedFields(['name', 'email', 'age', 'role']) && // data.name is string && data.name.size() > 0 && data.name.size() < 50 && // data.email is string && isValidEmail(data.email) && // data.age is number && data.age >= 18 && // data.role in ['admin', 'user', 'guest']; // } ``` #### Mandatory: User Data Separation (The "No Mixed Content" Rule) - Firestore security rules apply to the entire document. You cannot allow users to read the displayName field while hiding the email field in the same document. - If a collection (e.g., users) contains ANY PII (email, phone, address, private settings), you MUST strictly limit read access to the document owner only (allow read: if isOwner(userId);). - If the application requires public profiles (e.g., showing user names/avatars on posts): - 1. Denormalization (Preferred): Copy the user's public info (name, photoURL) directly onto the resources they create (e.g., store authorName and authorPhoto inside the posts document). - 2. Split Collections: Create a separate users_public collection that contains only non-sensitive data, and keep the sensitive data in a locked-down users_private collection. - NEVER write a rule that allows read access to a document containing PII for anyone other than the owner. #### **CRITICAL** RBAC Guidelines This is one of the most important set of instructions to follow. Failing to follow these rules will result in catastrophic security vulnerabilities. - **NEVER** allow users to create their own privileged roles. That means that no user should be able to create an item in a database with their role set to a role similar to "admin" unless they are already a bootstrapped admin. - **NEVER** allow users to update their own roles or permissions. - **NEVER** allow users to grant themselves access to other users' data. - **NEVER** allow users to bypass the role hierarchy. - **ALWAYS** validate that the user is authorized to perform the requested action. - **ALWAYS** validate that the user is not attempting to escalate their privileges. - **ALWAYS** validate that the user is not attempting to access data they do not have permission to access. Here's a **bad** example of what **NOT** to do: ```javascript match /users/{userId} { // BAD: Allows users to create their own roles because a user can create a new user document with a role of 'admin' and the isAdmin() function will return true allow create: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin(); // BAD: Allows users to update their own roles because a user can update their own user document with a role of 'admin' and the isAdmin() function will return true allow update: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin(); } ``` Here's a **good** example of what **TO** do: ```javascript match /users/{userId} { // GOOD: Does NOT allow users to create their own roles unless they are an admin or the user is updating their own role to a less privileged role allow create: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == 'client') || isAdmin()); // GOOD: Does NOT allow users to update their own roles unless they are an admin allow update: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == resource.data.role) || isAdmin()); } ``` #### Critical Directives for Secure Generation - **PREFER USING READ OVER LIST OR GET** `list` and `get` can add complexity to
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub