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— syncsdb_user_idclaim on user creationBusinessUsersService— syncsrole_by_business_idclaim on business user changesOnboardingOrchestratorService— setsonboarding_completedand initial role claimsUsersService— setsdb_user_idon user creation
Injection
| Token | Type | Description |
|---|---|---|
FirebaseService | Class | Concrete implementation (default for existing consumers) |
FIREBASE_AUTH_PORT | Interface | Port abstraction for new consumers and testing |
FIREBASE_APP | App | null | Raw Firebase Admin App instance |
Environment Variables
| Variable | Required | Description |
|---|---|---|
FIREBASE_PROJECT_ID | Yes | Firebase project ID |
FIREBASE_CLIENT_EMAIL | Yes | Service account email |
FIREBASE_PRIVATE_KEY | One of these | Service account private key (plain text, \n escaped) |
FIREBASE_PRIVATE_KEY_B64 | One of these | Service 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
| Error | When |
|---|---|
FirebaseNotInitializedError | Firebase credentials missing or invalid |
ClaimsTooLargeError | Merged claims exceed 1000-character Firebase limit |
Design Decisions
- No HTTP endpoints — This is a pure infrastructure module. HTTP auth endpoints live in the
authmodule. - Deep merge for claims — Uses
mergeDeepLeft(rambda) so partial claim updates don't destroy existing claims. Left (incoming) values take priority. - Graceful null-app — Allows the backend to start without Firebase credentials (useful for local dev/testing), failing only when Firebase operations are actually called.
- Port interface —
FirebaseAuthPortenables consumers to depend on an abstraction, improving testability without requiring a full Firebase emulator.