Skip to content

Commit 73f72f2

Browse files
committed
update documentation
1 parent b4c4395 commit 73f72f2

File tree

1 file changed

+48
-4
lines changed

1 file changed

+48
-4
lines changed

docs/Authentication.md

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ This document explains how server-side authentication works in the Mobility Data
44

55
## Overview
66
- Server components/actions call the Mobility Feed API using a server‑minted GCIP ID token.
7-
- The client never forwards its token to the server; all API calls use server credentials only.
7+
- The client never forwards its Firebase ID token to the server; all API calls use server credentials only.
8+
- A short‑lived HTTP‑only session cookie (`md_session`) stores a server‑signed JWT with the current user's identity (including guest/anonymous flag).
89
- Firebase Admin is initialized once via a centralized helper and used for both Remote Config and token minting.
910
- Mock mode (MSW) enables local development without hitting the real API or Firebase.
1011

@@ -16,8 +17,31 @@ This document explains how server-side authentication works in the Mobility Data
1617

1718
Key code paths:
1819
- `src/lib/firebase-admin.ts`: centralized Admin initialization (`getFirebaseAdminApp()`), backed by `ensureAdminInitialized()`.
19-
- `src/app/utils/auth-server.ts`: token functions `getGcipIdToken()` and `getSSRAccessToken()` (the canonical token provider for SSR calls).
20-
- `src/app/services/feeds/index.ts`: OpenAPI client; injects `Authorization` header using the token returned by `getSSRAccessToken()`.
20+
- `src/app/utils/auth-server.ts`: token functions `getGcipIdToken()` and `getSSRAccessToken()` (the canonical token provider for SSR calls) and helpers for reading the session cookie.
21+
- `src/app/services/feeds/index.ts`: OpenAPI client; injects `Authorization` and user‑context headers using the token returned by `getSSRAccessToken()`.
22+
23+
## Session Cookie & SSR User Identity
24+
25+
To let server components know "who" the current user is (including guests) without ever trusting client tokens directly, the web app uses a short‑lived, server‑signed session JWT stored in the `md_session` HTTP‑only cookie.
26+
27+
Flow:
28+
1. The user signs in on the client with Firebase Auth (email/password, provider, or anonymous).
29+
2. After any successful login, a Redux saga calls `setUserCookieSession()` from [src/app/services/session-service.ts](src/app/services/session-service.ts).
30+
3. `setUserCookieSession()` reads the current Firebase ID token from the client SDK and POSTs it to `/api/session`.
31+
4. The `/api/session` `POST` handler in [src/app/api/session/route.ts](src/app/api/session/route.ts):
32+
- Verifies the ID token with Firebase Admin.
33+
- Derives an `isGuest` flag from the sign‑in provider (`anonymous` → guest).
34+
- Issues a short‑lived session JWT (1 hour) signed with `NEXT_SESSION_JWT_SECRET` containing:
35+
- `uid`, optional `email`, `isGuest`, `iat`, and `exp`.
36+
- Sets the `md_session` cookie (HTTP‑only, `sameSite=lax`, `secure` in production).
37+
38+
On the server side:
39+
- [src/app/utils/session-jwt.ts](src/app/utils/session-jwt.ts) defines the `SessionPayload` type and helpers to sign/verify the JWT used in `md_session`.
40+
- [src/app/utils/auth-server.ts](src/app/utils/auth-server.ts) exposes:
41+
- `getCurrentUserFromCookie()` to decode the session cookie into `SessionPayload` for SSR.
42+
- `getUserContextJwtFromCookie()` to obtain the raw, verified session JWT for forwarding to the backend.
43+
44+
The GCIP ID token used for IAP remains a server‑minted token that does not depend on the client token; the session cookie is only used for identifying the current end‑user (including guests) and for per‑user attribution.
2145

2246
## Firebase Admin Initialization
2347
Centralized in `getFirebaseAdminApp()`:
@@ -33,6 +57,7 @@ Server‑side credentials and config (server‑only):
3357
- `GOOGLE_SA_JSON`: Inline service account JSON string.
3458
- `GOOGLE_SA_JSON_PATH`: Absolute/relative path to service account JSON file.
3559
- `NEXT_PUBLIC_FIREBASE_PROJECT_ID`: Project ID; used to match Admin apps and as fallback when JSON lacks `project_id`.
60+
- `NEXT_SESSION_JWT_SECRET`: Secret used to sign and verify the `md_session` session JWT on the web side.
3661

3762
GCIP / Identity Toolkit:
3863
- `GCIP_API_KEY` (or `FIREBASE_API_KEY` or `NEXT_PUBLIC_FIREBASE_API_KEY`): API key for `accounts:signInWithCustomToken`.
@@ -43,6 +68,8 @@ Mock/dev:
4368
- `NEXT_PUBLIC_API_MOCKING=enabled`: Enables MSW mock service worker in the browser.
4469
- `LOCAL_DEV_NO_ADMIN=1` (optional): Bypass Admin initialization for Remote Config/token code paths during local experimentation.
4570

71+
> Note: On the Mobility Feed API (Python) side, a matching secret (e.g. `S2S_JWT_SECRET`) is used to validate the user‑context JWT forwarded from the web app.
72+
4673
## Remote Config
4774
- Server‑side code fetches Firebase Remote Config via Admin SDK.
4875
- In mock mode, Remote Config returns defaults to avoid Admin calls.
@@ -68,10 +95,27 @@ Setup:
6895
- Do **not** forward client tokens to the server.
6996
- Keep all credentials server‑only and never expose service account JSON to client code.
7097

98+
### End‑User Context Propagation to the Mobility Feed API
99+
100+
In addition to the GCIP ID token for IAP, SSR API calls also propagate a compact, server‑signed user‑context JWT so the backend can attribute requests to an end‑user without trusting any client tokens:
101+
102+
- The `md_session` cookie's JWT is reused as this user‑context token.
103+
- On the server, [src/app/utils/auth-server.ts](src/app/utils/auth-server.ts) reads the cookie via `getUserContextJwtFromCookie()`.
104+
- [src/app/context/api-auth-middleware.ts](src/app/context/api-auth-middleware.ts) provides `generateAuthMiddlewareWithToken(accessToken, userContextJwt?)`, which:
105+
- Sets `Authorization: Bearer <accessToken>` for IAP.
106+
- When `userContextJwt` is present, also sets `x-mdb-user-context: <userContextJwt>`.
107+
- All server‑side feeds service functions in [src/app/services/feeds/index.ts](src/app/services/feeds/index.ts) accept an optional `userContextJwt` and pass it into this middleware.
108+
109+
On the Mobility Feed API side (see [mobility-feed-api/api/src/middleware/request_context.py](mobility-feed-api/api/src/middleware/request_context.py)):
110+
- The `RequestContext` middleware reads `x-mdb-user-context`.
111+
- It verifies the HS256 signature using a shared secret and decodes the payload.
112+
- It populates `user_id`, `user_email`, and an `is_guest` flag for auditing and per‑user behavior.
113+
71114
## Security Considerations
72115
- **No client token pass‑through**: Prevents elevation of privilege and token replay.
73116
- **Server‑only credentials**: Service account material must never be sent to the client.
74-
- **Optional auditing**: You may forward end‑user identity headers (e.g., `X-User-Subject`, `X-User-Email`) for backend audit trails if required.
117+
- **End‑user attribution without client tokens**: The backend receives only a server‑signed, minimal user‑context JWT via `x-mdb-user-context`, never the raw client Firebase ID token.
118+
- **Guest users**: Anonymous sign‑ins are explicitly flagged via `isGuest` in the session JWT so both the web app and backend can distinguish guest from authenticated accounts.
75119

76120
## Troubleshooting
77121
Common issues and fixes:

0 commit comments

Comments
 (0)