Skip to content
Back to skills

Firestore Security Rules

ASecurity

Write and validate Firestore Security Rules following the project's multi-tenancy Blueprint pattern. Use this skill when implementing collection-level security, Blueprint membership validation, role-based permissions, and data access controls. Ensures rules validate BlueprintMember status, check permissions array, enforce data isolation, and integrate with the three-layer architecture where Security Rules are the first line of defense.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
securityjavascripttypescriptrustgojavabashtestinggitdatabasesecurity

Works with

  • cli

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill firestore-security-rules --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Firestore Security Rules?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Firestore Security Rules
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-firestore-security-rules/badge)](https://www.skillsdirectory.com/skills/majiayu000-firestore-security-rules)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "Firestore Security Rules"
description: "Write and validate Firestore Security Rules following the project's multi-tenancy Blueprint pattern. Use this skill when implementing collection-level security, Blueprint membership validation, role-based permissions, and data access controls. Ensures rules validate BlueprintMember status, check permissions array, enforce data isolation, and integrate with the three-layer architecture where Security Rules are the first line of defense."
license: "MIT"
---

# Firestore Security Rules Skill

This skill helps create secure Firestore Security Rules following the project's multi-tenancy architecture.

## Core Principles

### Security-First Architecture
- **Security Rules are First Defense**: Never trust client-side checks alone
- **Multi-Tenancy**: Blueprint-based data isolation
- **Permission System**: Role + permissions array validation
- **Server-Side Validation**: All access validated at database level

### Blueprint Multi-Tenancy Model

```
User → Organization → Blueprint → Resources
           ↓
      Team / Partner
```

- Blueprint defines permission boundaries (not data boundaries)
- BlueprintMember is dedicated collection for membership
- All resources belong to a Blueprint
- Access controlled via BlueprintMember role + permissions

## Core Helper Functions

### Global Authentication

```javascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    
    /**
     * Check if user is authenticated
     */
    function isAuthenticated() {
      return request.auth != null;
    }
    
    /**
     * Get current user ID
     */
    function getCurrentUserId() {
      return request.auth.uid;
    }
    
    /**
     * Check if user is system admin
     */
    function isSystemAdmin() {
      return isAuthenticated() && 
             get(/databases/$(database)/documents/users/$(getCurrentUserId())).data.role == 'admin';
    }
  }
}
```

### Blueprint Membership Functions

```javascript
/**
 * Check if user is Blueprint member
 */
function isBlueprintMember(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  return exists(/databases/$(database)/documents/blueprintMembers/$(memberId));
}

/**
 * Get user's role in Blueprint
 */
function getBlueprintMemberRole(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  return member.data.role;
}

/**
 * Check if user is Owner or Admin
 */
function isBlueprintOwnerOrAdmin(blueprintId) {
  let role = getBlueprintMemberRole(blueprintId);
  return role in ['owner', 'admin'];
}

/**
 * Check if user has specific permission
 */
function hasPermission(blueprintId, permission) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  return permission in member.data.permissions;
}

/**
 * Check if member is active
 */
function isMemberActive(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  return member.data.status == 'active';
}
```

### Data Validation Functions

```javascript
/**
 * Validate task data structure
 */
function validateTaskData(data) {
  return data.keys().hasAll(['blueprintId', 'title', 'status']) &&
         data.title is string &&
         data.title.size() > 0 &&
         data.title.size() <= 200 &&
         data.status in ['pending', 'in-progress', 'completed', 'archived'];
}
```

## Collection Rules Pattern

### Resource Collection (with Blueprint)

```javascript
match /tasks/{taskId} {
  // Blueprint members can read tasks
  allow read: if isAuthenticated() && 
                 isBlueprintMember(resource.data.blueprintId) &&
                 isMemberActive(resource.data.blueprintId);
  
  // Members with task:create permission can create
  allow create: if isAuthenticated() && 
                   isBlueprintMember(request.resource.data.blueprintId) &&
                   isMemberActive(request.resource.data.blueprintId) &&
                   hasPermission(request.resource.data.blueprintId, 'task:create') &&
                   validateTaskData(request.resource.data);
  
  // Members with task:update permission or assignee can update
  allow update: if isAuthenticated() && 
                   isBlueprintMember(resource.data.blueprintId) &&
                   isMemberActive(resource.data.blueprintId) &&
                   (hasPermission(resource.data.blueprintId, 'task:update') || 
                    resource.data.assignedTo == getCurrentUserId()) &&
                   validateTaskData(request.resource.data);
  
  // Only members with task:delete permission can delete
  allow delete: if isAuthenticated() && 
                   isBlueprintMember(resource.data.blueprintId) &&
                   isMemberActive(resource.data.blueprintId) &&
                   hasPermission(resource.data.blueprintId, 'task:delete');
}
```

### Nested Subcollections

```javascript
match /blueprints/{blueprintId} {
  allow read: if isAuthenticated() && isBlueprintMember(blueprintId);
  
  // Nested tasks subcollection
  match /tasks/{taskId} {
    allow read: if isAuthenticated() && 
                   isBlueprintMember(blueprintId) &&
                   isMemberActive(blueprintId);
    
    allow create: if isAuthenticated() && 
                     isBlueprintMember(blueprintId) &&
                     isMemberActive(blueprintId) &&
                     hasPermission(blueprintId, 'task:create') &&
                     validateTaskData(request.resource.data) &&
                     request.resource.data.blueprintId == blueprintId;
  }
}
```

### Membership Collection

```javascript
match /blueprintMembers/{memberId} {
  // Blueprint members can read member list
  allow read: if isAuthenticated() && 
                 isBlueprintMember(resource.data.blueprintId);
  
  // Only Owner/Admin can add/modify/remove members
  allow create, update: if isAuthenticated() && 
                           isBlueprintOwnerOrAdmin(resource.data.blueprintId);
  
  // Only Owner can delete members (soft delete recommended)
  allow delete: if isAuthenticated() && 
                   isBlueprintOwnerOrAdmin(resource.data.blueprintId);
}
```

### User Collection

```javascript
match /users/{userId} {
  // Users can only read their own data
  allow read: if isAuthenticated() && getCurrentUserId() == userId;
  
  // Users can update their own data (but not role)
  allow update: if isAuthenticated() && 
                   getCurrentUserId() == userId &&
                   !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role']);
  
  // Only system admin can create/delete users
  allow create, delete: if isSystemAdmin();
}
```

### Audit Logs (Write-Only)

```javascript
match /auditLogs/{logId} {
  // Only system admin can read logs
  allow read: if isSystemAdmin();
  
  // Any authenticated user can write logs
  allow create: if isAuthenticated();
  
  // Logs cannot be modified or deleted
  allow update, delete: if false;
}
```

## Performance Optimization

### Minimize get() Calls

```javascript
// ❌ BAD: Multiple get() calls
function hasMultiplePermissions(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  let canCreate = 'task:create' in member.data.permissions;
  let member2 = get(/databases/$(database)/documents/blueprintMembers/$(memberId)); // Duplicate!
  let canUpdate = 'task:update' in member2.data.permissions;
  return canCreate && canUpdate;
}

// ✅ GOOD: Single get() call
function hasMultiplePermissions(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  return 'task:create' in member.data.permissions && 
         'task:update' in member.data.permissions;
}
```

### Use exists() When Possible

```javascript
// ✅ GOOD: exists() is faster than get()
function isBlueprintMember(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  return exists(/databases/$(database)/documents/blueprintMembers/$(memberId));
}

// Only use get() when you need data
function getBlueprintMemberRole(blueprintId) {
  let memberId = getCurrentUserId() + '_' + blueprintId;
  let member = get(/databases/$(database)/documents/blueprintMembers/$(memberId));
  return member.data.role;
}
```

## Testing Security Rules

### Using Firebase Emulator

```bash
# Start emulator
firebase emulators:start

# Run security rules tests
npm run test:rules
```

### Test Template

```javascript
describe('Task Collection Security Rules', () => {
  it('should allow authenticated blueprint member to read tasks', async () => {
    const db = testEnv.authenticatedContext('user1').firestore();
    
    // Setup: Create member
    await testEnv.withSecurityRulesDisabled(async (context) => {
      await context.firestore().doc('blueprintMembers/user1_blueprint1').set({
        blueprintId: 'blueprint1',
        userId: 'user1',
        role: 'member',
        status: 'active',
        permissions: ['task:read']
      });
    });
    
    // Test: Read should succeed
    await assertSucceeds(db.doc('tasks/task1').get());
  });
  
  it('should deny unauthenticated access', async () => {
    const db = testEnv.unauthenticatedContext().firestore();
    await assertFails(db.doc('tasks/task1').get());
  });
});
```

## Checklist

When writing Security Rules:

- [ ] All collections have security rules
- [ ] Multi-tenancy data isolation implemented
- [ ] BlueprintMember membership checked
- [ ] Permissions array validated
- [ ] Member status checked (active/suspended)
- [ ] Data validation functions complete
- [ ] Minimize get() calls for performance
- [ ] Use exists() when only checking existence
- [ ] Audit logs are write-only
- [ ] Cross-blueprint access prevented
- [ ] Tests cover all scenarios
- [ ] Both positive and negative tests

## Integration with Architecture

### Three-Layer Security

```
1. UI Layer: Permission checks for UX (can hide buttons)
2. Service Layer: Business logic validation
3. Security Rules: Server-side enforcement (MUST)
```

### Repository + Security Rules

```typescript
// Repository - No permission checks
async deleteTask(taskId: string): Promise<void> {
  await this.taskRepository.delete(taskId);
  // Security Rules will validate permissions
}
```

```javascript
// Security Rules - Permission enforcement
match /tasks/{taskId} {
  allow delete: if isAuthenticated() 
                && isBlueprintMember(resource.data.blueprintId)
                && hasPermission(resource.data.blueprintId, 'task:delete');
}
```

## References

- [Security Rules Instructions](.github/instructions/ng-gighub-security-rules.instructions.md)
- [Architecture Guide](.github/instructions/ng-gighub-architecture.instructions.md)
- [Firebase Documentation](https://firebase.google.com/docs/firestore/security/get-started)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.