Authentication
The server uses Firebase Authentication to verify requests. Every protected route expects a Firebase ID token in the Authorization header.
How it works
- The client signs in via Firebase (web SDK, mobile SDK, etc.) and obtains an ID token.
- The client sends the token on every request:
Authorization: Bearer <firebase-id-token> - The
requireAuthmiddleware (apps/server/src/middleware/auth.middleware.ts) callsadminAuth.verifyIdToken(). - On success, the decoded token is attached to
req.firebaseUserand the request continues. - On failure, the middleware responds immediately —
401for a missing/malformed header,403for 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.