Saltar al contenido principal

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 identity/platform claims (db_user_id, role, onboarding_completed). Always strips role_by_business_id — membership roles live in business_user and are resolved by RolesGuard / MembershipRoleLookup. Validates the slim payload does not exceed Firebase's 1000-character limit.

Slim JWT contract:

ClaimPurpose
db_user_idFlowPOS user UUID
roleOptional platform role (root / admin)
onboarding_completedOnboarding flag

Consumers:

  • AuthService — syncs db_user_id on user creation; may set platform role
  • OnboardingOrchestratorService — sets db_user_id / onboarding_completed
  • UserIdentityService — sets db_user_id on user creation

Do not write membership roles into Firebase claims.

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
ClaimsTooLargeErrorSlim claims payload still exceeds 1000 characters

Design Decisions

  1. No HTTP endpoints — This is a pure infrastructure module. HTTP auth endpoints live in the auth module.
  2. Deep merge + strip membership map — Uses mergeDeepLeft (rambda) so partial claim updates don't destroy unrelated claims. Legacy role_by_business_id is removed on every write so multi-business users stay under Firebase's 1KB limit.
  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.