Skip to content

Authentication

The server uses Firebase Authentication to verify requests. Every protected route expects a Firebase ID token in the Authorization header.

How it works

  1. The client signs in via Firebase (web SDK, mobile SDK, etc.) and obtains an ID token.
  2. The client sends the token on every request: Authorization: Bearer <firebase-id-token>
  3. The requireAuth middleware (apps/server/src/middleware/auth.middleware.ts) calls adminAuth.verifyIdToken().
  4. On success, the decoded token is attached to req.firebaseUser and the request continues.
  5. On failure, the middleware responds immediately — 401 for a missing/malformed header, 403 for an invalid or expired token.

Protecting a route

Add requireAuth as a middleware argument before your handler:

import { Router } from "express";
import { requireAuth } from "../middleware/auth.middleware.js";

export const myRouter = Router();

myRouter.get("/", requireAuth, async (req, res) => {
  const uid = req.firebaseUser!.uid;
  res.json({ uid });
});

req.firebaseUser is a DecodedIdToken from firebase-admin/auth — it contains uid, email, custom claims, and more.

Environment variables

Variable Required Description
FIREBASE_SERVICE_ACCOUNT_BASE64 No* Base64-encoded service account JSON from Firebase Console

*If omitted, the SDK falls back to GOOGLE_APPLICATION_CREDENTIALS or the GCP metadata server (Application Default Credentials).

Generating the base64 value

base64 -w0 serviceAccountKey.json

Paste the output into apps/server/.env:

FIREBASE_SERVICE_ACCOUNT_BASE64=<output>

Forwarding auth to the AI service

aiServiceFetch in apps/server/src/lib/ai-service.ts forwards the Authorization header and a correlation ID to the Python service automatically — no extra work needed in controllers.

const upstream = await aiServiceFetch(req, "/some-endpoint");

The AI service itself does not re-verify the token; it trusts the server as the only caller (the service is not exposed publicly in Docker Compose), secure by design.