You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/Authentication.md
+48-4Lines changed: 48 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,8 @@ This document explains how server-side authentication works in the Mobility Data
4
4
5
5
## Overview
6
6
- 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).
8
9
- Firebase Admin is initialized once via a centralized helper and used for both Remote Config and token minting.
9
10
- Mock mode (MSW) enables local development without hitting the real API or Firebase.
10
11
@@ -16,8 +17,31 @@ This document explains how server-side authentication works in the Mobility Data
16
17
17
18
Key code paths:
18
19
-`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`.
-`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.
21
45
22
46
## Firebase Admin Initialization
23
47
Centralized in `getFirebaseAdminApp()`:
@@ -33,6 +57,7 @@ Server‑side credentials and config (server‑only):
33
57
-`GOOGLE_SA_JSON`: Inline service account JSON string.
34
58
-`GOOGLE_SA_JSON_PATH`: Absolute/relative path to service account JSON file.
35
59
-`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.
36
61
37
62
GCIP / Identity Toolkit:
38
63
-`GCIP_API_KEY` (or `FIREBASE_API_KEY` or `NEXT_PUBLIC_FIREBASE_API_KEY`): API key for `accounts:signInWithCustomToken`.
@@ -43,6 +68,8 @@ Mock/dev:
43
68
-`NEXT_PUBLIC_API_MOCKING=enabled`: Enables MSW mock service worker in the browser.
44
69
-`LOCAL_DEV_NO_ADMIN=1` (optional): Bypass Admin initialization for Remote Config/token code paths during local experimentation.
45
70
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
+
46
73
## Remote Config
47
74
- Server‑side code fetches Firebase Remote Config via Admin SDK.
48
75
- In mock mode, Remote Config returns defaults to avoid Admin calls.
@@ -68,10 +95,27 @@ Setup:
68
95
- Do **not** forward client tokens to the server.
69
96
- Keep all credentials server‑only and never expose service account JSON to client code.
70
97
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()`.
- 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
+
71
114
## Security Considerations
72
115
-**No client token pass‑through**: Prevents elevation of privilege and token replay.
73
116
-**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.
0 commit comments