Skip to main content

Firebase Module

Shared infrastructure module wrapping the Firebase Admin SDK. Provides token verification and custom claims management to other backend modules.

Architecture

firebase/
├── firebase.module.ts # NestJS module definition
├── domain/
│ ├── firebase-auth.port.ts # Port interface (FirebaseAuthPort)
│ ├── firebase.types.ts # Shared type definitions
│ └── firebase.errors.ts # Domain error classes
└── infrastructure/
├── firebase.service.ts # Concrete adapter (implements FirebaseAuthPort)
└── firebase.providers.ts # Factory providers (Firebase App + port binding)

Domain layer — Framework-agnostic port interface (FirebaseAuthPort), types, and error classes. Consumers can depend on the port abstraction for testability.

Infrastructure layer — Concrete FirebaseService implementing the port using Firebase Admin SDK. Provider factory handles SDK initialization from environment variables.

Capabilities

Token Verification

verifyIdToken(idToken: string): Promise<DecodedIdToken>

Verifies a Firebase ID token and returns the decoded payload. Used by AuthGuard (globally) and AuthService (auth flow).

Custom Claims Management

updateUserCustomClaims({ uid, claims }): Promise<void>

Deep-merges new claims onto the user's existing Firebase custom claims using mergeDeepLeft. Preserves unrelated claims. Validates the merged payload does not exceed Firebase's 1000-character limit.

Consumers:

  • AuthService — syncs db_user_id claim on user creation
  • BusinessUsersService — syncs role_by_business_id claim on business user changes
  • OnboardingOrchestratorService — sets onboarding_completed and initial role claims
  • UsersService — sets db_user_id on user creation

Injection

TokenTypeDescription
FirebaseServiceClassConcrete implementation (default for existing consumers)
FIREBASE_AUTH_PORTInterfacePort abstraction for new consumers and testing
FIREBASE_APPApp | nullRaw Firebase Admin App instance

Environment Variables

VariableRequiredDescription
FIREBASE_PROJECT_IDYesFirebase project ID
FIREBASE_CLIENT_EMAILYesService account email
FIREBASE_PRIVATE_KEYOne of theseService account private key (plain text, \n escaped)
FIREBASE_PRIVATE_KEY_B64One of theseService account private key (base64 encoded, takes precedence)

If credentials are missing, the module initializes with a null app and logs a warning. All operations will throw FirebaseNotInitializedError.

Error Classes

ErrorWhen
FirebaseNotInitializedErrorFirebase credentials missing or invalid
ClaimsTooLargeErrorMerged claims exceed 1000-character Firebase limit

Design Decisions

  1. No HTTP endpoints — This is a pure infrastructure module. HTTP auth endpoints live in the auth module.
  2. Deep merge for claims — Uses mergeDeepLeft (rambda) so partial claim updates don't destroy existing claims. Left (incoming) values take priority.
  3. Graceful null-app — Allows the backend to start without Firebase credentials (useful for local dev/testing), failing only when Firebase operations are actually called.
  4. Port interfaceFirebaseAuthPort enables consumers to depend on an abstraction, improving testability without requiring a full Firebase emulator.