HIGH insecure designadonisjsfirestore

Insecure Design in Adonisjs with Firestore

Insecure Design in Adonisjs with Firestore — how this specific combination creates or exposes the vulnerability

Insecure design in an AdonisJS application that uses Google Cloud Firestore often stems from modeling data and access patterns that do not enforce authorization at the data layer. Firestore’s flexible document model can encourage storing user-scoped data in flat collections without enforcing ownership checks in every query. When route handlers or controller actions build queries using only client-supplied identifiers (such as a resource ID) without verifying that the authenticated user has permission to access that document, this becomes a Broken Object Level Authorization (BOLA) / Insecure Direct Object Reference (IDOR) pattern.

For example, an AdonisJS route like /projects/:projectId/members might directly use projectId to fetch a document from a projects collection. If the handler does not ensure the authenticated user is a member of that project, an attacker can iterate or guess project IDs and read or modify data they should not see. This risk is amplified when Firestore security rules rely on request.auth.uid but the application layer does not perform matching checks, leading to inconsistent enforcement.

Another insecure design pattern is trusting Firestore document IDs as authorization tokens. Document IDs are often predictable (e.g., push IDs or sequential values), and if the application assumes that possessing a document ID implies access, an attacker can leverage IDOR to traverse relationships. For instance, user profiles stored under a users collection with IDs derived from user registration order can be enumerated. Even when Firestore rules restrict reads to request.auth.uid, an AdonisJS service that passes attacker-controlled IDs directly to the backend can bypass intended boundaries if the service does not validate ownership.

Data exposure can also arise from how AdonisJS structures its models and services. If a service method returns entire Firestore documents without filtering sensitive fields (such as password hashes, internal flags, or PII), the API response may leak data. This is a data exposure design flaw: the application layer should project only necessary fields and apply strict allowlists before serialization. Similarly, insufficient rate limiting or missing validation on input identifiers can enable enumeration attacks, where attackers infer valid resource IDs by observing differences in response times or status codes.

These issues map to common frameworks such as the OWASP API Top 10 (2023), specifically Broken Object Level Authorization and Excessive Data Exposure. PCI-DSS, SOC 2, and GDPR also require that access controls be enforced consistently and that personal data be protected against unauthorized access. In AdonisJS with Firestore, this means designing services that treat Firestore document references as opaque tokens, always pairing application-level ownership checks with Firestore rules, and never relying on a single layer for authorization.

Firestore-Specific Remediation in Adonisjs — concrete code fixes

To remediate insecure design when using AdonisJS with Firestore, enforce strict ownership checks in service methods and ensure query constraints always include the authenticated user’s UID. Below are concrete patterns that demonstrate secure handling of Firestore documents in AdonisJS controllers and services.

1. Parameterized query with ownership check

Always scope queries to the authenticated user. Do not trust route parameters alone. In an AdonisJS controller, bind the user ID from the authentication guard and use it as a Firestore query filter:

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Project from 'App/Models/Project'

export default class ProjectsController {
  public async show({ params, auth }: HttpContextContract) {
    const user = auth.user!
    const project = await Project.query()
      .where('id', params.id)
      .where('userId', user.id) // enforce ownership at query time
      .preload('members')
      .firstOrFail()
    return project
  }
}

This ensures that even if an attacker manipulates params.id, the query will return no results unless the document belongs to the authenticated user.

2. Firestore rule + service-layer validation alignment

Design Firestore security rules to match application guarantees. For a projects collection where only members can read documents, rules should reference a members subcollection and the AdonisJS service should verify membership before returning data:

// Firestore security rule (conceptual)
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /projects/{projectId} {
      allow read: if request.auth != null
        && exists(/databases/$(database)/documents/projects/$(projectId)/members/$(request.auth.uid));
      allow write: if request.auth != null
        && exists(/databases/$(database)/documents/projects/$(projectId)/members/$(request.auth.uid));
    }
  }
}

In AdonisJS, a service method can double-check membership by reading the membership subcollection:

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class ProjectService {
  public async userCanAccessProject(userId: string, projectId: string): Promise {
    const memberDoc = await firestore
      .doc(`projects/${projectId}/members/${userId})
      .get()
    return memberDoc.exists
  }
}

This two-layer validation reduces the risk of misconfigured rules or client-side tampering.

3. Output filtering and field-level permissions

Prevent data exposure by selecting only required fields and removing sensitive data before serialization. Avoid returning entire Firestore documents directly from controllers:

import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class ProfilesController {
  public async show({ params, auth }: HttpContextContract) {
    const doc = await firestore.doc(`users/${params.id}`).get()
    const data = doc.data()
    if (!data || data.publicId !== auth.user?.publicId) {
      throw new Error('Unauthorized')
    }
    // Return only safe fields
    return {
      publicId: data.publicId,
      displayName: data.displayName,
      email: data.email,
      // Never include passwordHash, internalRole, or rawTokens
    }
  }
}

This approach enforces a strict allowlist and prevents accidental leakage of credentials or internal metadata.

4. Input validation and canonical resource references

Validate identifiers to avoid enumeration and ensure they are not guessable. Use UUIDs or opaque tokens instead of sequential IDs where appropriate, and normalize references in Firestore paths:

import { v4 as uuidv4 } from 'uuid'

export async function createProject(authUserId: string, payload: any) {
  const projectId = uuidv4() // opaque, non-enumerable ID
  await firestore.doc(`projects/${projectId}`).set({
    id: projectId,
    userId: authUserId,
    name: payload.name,
    createdAt: new Date(),
  })
  return projectId
}

By using non-sequential IDs and ensuring the backend owns the mapping between business keys and Firestore paths, you reduce the risk of IDOR via predictable references.

Frequently Asked Questions

How does middleBrick handle insecure design findings in AdonisJS with Firestore?
middleBrick detects and reports insecure design patterns such as missing ownership checks and data exposure when scanning an API endpoint. It provides prioritized findings with severity levels and remediation guidance, but it does not fix, patch, or block anything. You can run scans via the CLI, dashboard, or GitHub Action to surface these issues.
Can Firestore security rules alone protect against IDOR when used with AdonisJS?
Firestore security rules are an important layer, but they should not be the sole authorization mechanism. Application-level checks in AdonisJS services must also enforce ownership and scope queries to the authenticated user’s data. middleBrick’s checks include both runtime behavior and spec analysis to highlight gaps where rules alone are insufficient.