HIGH broken access controladonisjsfirestore

Broken Access Control in Adonisjs with Firestore

Broken Access Control in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability

Broken Access Control occurs when an API fails to enforce proper authorization between subjects (users or services) and resources. In a typical Adonisjs application that uses Google Cloud Firestore as the backend, this risk arises when route-level or controller-level checks are missing, incomplete, or bypassed, and Firestore security rules are misconfigured or treated as the sole authorization boundary.

Adonisjs does not include built-in Firestore integration; developers typically interact with Firestore via the official Node.js SDK. If authorization logic lives only in Firestore rules and is not mirrored or enforced in Adonisjs controllers, an attacker who bypasses application routes (for example, by discovering an unguarded endpoint or guessing an identifier) can invoke Firestore operations directly. Conversely, if Adonisjs enforces authorization but Firestore rules are over-permissive (e.g., allowing read/write based only on authentication tokens without validating resource ownership), an authenticated user can access or modify data belonging to other users.

A concrete scenario: an Adonisjs route like /api/users/:userId/profile retrieves a document using the route parameter userId. If the controller uses the authenticated user’s identity to construct a Firestore path such as users/{userId} but does not verify that the authenticated user matches userId, horizontal privilege escalation occurs. An attacker can change userId in the request to access another user’s profile. Because Firestore rules might permit read access to any authenticated user (e.g., allowing reads if request.auth != null), the vulnerability is exposed at the intersection of Adonisjs routing and Firestore permissions.

Additionally, Firestore’s flexible querying can inadvertently expose data if queries are constructed with insufficient scoping. For example, an admin-only endpoint that does not restrict the query by role or tenant could return documents across all users. In a multi-tenant setup, missing tenant IDs in queries or missing ownership checks in Firestore rules can lead to Insecure Direct Object References (IDOR), a common form of Broken Access Control. The risk is compounded when Firestore security rules use wildcard permissions for development and are not tightened for production, or when custom claims or tokens are not validated in Adonisjs before generating Firestore access tokens.

Broken Access Control in this stack is also about insecure direct object references across related collections. If an Adonisjs service retrieves a user’s documents without validating that the document’s owner field matches the authenticated subject, and Firestore rules do not enforce ownership constraints, an attacker can traverse relationships (e.g., from a user to their files to shared links) to access data outside their permissions. The unique exposure with Firestore is its real-time listeners and caching behavior: an over-permissive listener can push sensitive data to unauthorized clients if the application layer does not filter or validate before attaching listeners.

To assess this risk, scans inspect whether authorization checks exist in Adonisjs routes and whether they align with Firestore security rules. They look for missing ownership validation, missing role/tenant scoping, wildcard rules, and endpoints that accept user-supplied identifiers without verifying they belong to the requesting subject. Findings include insecure direct object references, privilege escalation via modified identifiers, and excessive data exposure due to misconfigured rules.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

Remediation centers on enforcing authorization in Adonisjs and tightening Firestore security rules so that each layer acts as a checkpoint rather than a single point of failure. Below are concrete, realistic examples for Adonisjs using the Firestore Node.js SDK.

1. Enforce ownership in Adonisjs before Firestore operations

Always resolve the authenticated user’s identifier and compare it with the resource’s owner field. Do not rely on Firestore rules alone to enforce ownership for sensitive operations.

// controllers/UserProfileController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { Firestore, doc, getDoc } from 'firebase-admin/firestore'

export default class UserProfileController {
  public async show({ params, auth }: HttpContextContract) {
    const userId = auth.user?.id
    const requestedUserId = params.userId

    if (!userId || userId !== requestedUserId) {
      throw new Error('Unauthorized: profile access denied')
    }

    const db: Firestore = require('firebase-admin').firestore()
    const userRef = doc(db, 'users', userId)
    const snapshot = await getDoc(userRef)

    if (!snapshot.exists) {
      throw new Error('Not found')
    }

    return snapshot.data()
  }
}

2. Scope Firestore queries by tenant and role

When retrieving lists, include tenant or role filters both in the application logic and in security rules. This prevents horizontal data leakage across tenants.

// services/DocumentService.ts
import { Firestore, collection, query, where, getDocs } from 'firebase-admin/firestore'

export const listUserDocuments = async (user: any, tenantId: string) => {
  const db = Firestore()
  const docsCol = collection(db, 'tenants', tenantId, 'documents')
  const q = query(docsCol, where('ownerId', '==', user.id), where('role', 'in', [user.role, 'admin']))
  const snapshot = await getDocs(q)
  return snapshot.docs.map(d => d.data())
}

3. Tighten Firestore security rules to mirror application checks

Security rules should validate authentication, resource ownership, and tenant context. Avoid wildcard read/write permissions in production.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /tenants/{tenantId}/documents/{docId} {
      allow read, write: if request.auth != null
        && request.auth.token.tenantId == tenantId
        && (request.auth.token.role == 'admin' || request.auth.uid == request.resource.data.ownerId);
    }
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

4. Validate and sanitize identifiers

Treat route parameters as untrusted. Validate format and ownership before using them in Firestore paths to prevent IDOR via parameter tampering.

// middleware/validateUserId.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { isAlphanumeric, isLength } from 'validator'

export default async function validateUserId({ params }: HttpContextContract) {
  const userId = params.userId
  if (!isAlphanumeric(userId) || !isLength(userId, { min: 20, max: 20 })) {
    throw new Error('Invalid user identifier')
  }
}

5. Use custom claims for role and tenant scoping

Set and verify custom claims on ID tokens to enforce role-based and tenant-based access in both Adonisjs and Firestore rules.

// Assigning custom claims (admin example)
import { getAuth } from 'firebase-admin/auth'
await getAuth().setCustomUserClaims(uid, { role: 'admin', tenantId: 'acme' })

// In Firestore rules
allow read: if request.auth.token.role == 'admin' && request.auth.token.tenantId == 'acme';

6. Avoid exposing Firestore internals to clients

Do not return raw Firestore references or internal IDs directly to clients. Use DTOs and enforce access checks before serialization.

// DTO transformation in controller
export const toPublicProfile = (data: any) => ({
  id: data.id,
  name: data.name,
  email: data.email,
  // do not expose internal paths or admin flags
})

Frequently Asked Questions

Can Firestore security rules alone prevent Broken Access Control in Adonisjs?
No. Firestore rules are a last line of defense but should not be the only control. Adonisjs must enforce authorization, validate identifiers, and scope queries to prevent IDOR and privilege escalation, because rules may be misconfigured or bypassed.
How does middleBrick detect Broken Access Control in this stack?
middleBrick runs unauthenticated scans that inspect whether authorization checks exist in Adonisjs routes and whether they align with Firestore security rules. It identifies missing ownership validation, wildcard rules, and endpoints that accept user-supplied identifiers without verifying resource ownership.