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
docs(adr): ADR-0034 cloud-mode Cognito JWT consumption + ROADMAP Phase 4e
Accepts ADR-0034 recording how aegis-core consumes the Cognito User Pool
LDZ is provisioning in staging/auth/ (ldz ADR-026 Partially Accepted per
cross-repo aegis-core#76, 2026-04-23). Four decisions:
- D1 Gateway JWT validation: lestrrat-go/jwx/v2 + in-memory JWKS cache,
new auth.OIDCProvider implementing the existing auth.Provider port
- D2 SPA OAuth: react-oidc-context + Cognito Hosted UI, memory-only
token storage (XSS mitigation), provider-agnostic wrapper
- D3 custom:tenant_id propagation: reuses existing Principal.TenantID
wire from Phase 2 A2; ADR-0022 Qdrant filter is the consumer
- D4 Testing: unit (mock JWKS httptest) + integration (nightly Dev User
Pool) + optional Playwright E2E, per Rule 2 test-first discipline
LOCAL mode unchanged — NoOpProvider preserved. DEPLOY_MODE env var
switches provider at gateway startup (local / cloud / cloud-test),
mirroring ADR-0031's LOCAL escape-hatch pattern. Phase 2
StaticJWTProvider preserved for the cloud-test scenario.
Phase 4e added to ROADMAP.md with four slices (4e-1 gateway middleware,
4e-2 SPA scaffold, 4e-3 tenant_id propagation verification, 4e-4
integration + optional E2E). First slice PR (4e-1 OIDCProvider + mock
JWKS unit tests) follows in a separate branch.
Three Open Questions flagged in ADR-0034 for implementation-time
decisions:
1. frontend_web/src/lib/auth.ts relationship with new useAuth() hook
2. Whether gateway_go/internal/pipeline/ already forwards tenant_id
3. Which SPA routes require auth (host vs viewer)
Refs: aegis-core#76, aegis-core#58, ADR-0022, ADR-0031
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: ROADMAP.md
+38Lines changed: 38 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -350,6 +350,44 @@ Gated by 3b — prompter display needs real transcript data; corpus selector nee
350
350
-[ ]~~Grafana dashboards and PagerDuty alerts provisioned by `aegis-aws-landing-zone` repository~~ — **superseded 2026-04-21** by C-Obs-2 above. Original self-hosted kube-prometheus-stack model had the platform authoring dashboards; the Grafana Cloud reversal (ldz ADR-022 + ADR-023) shifts dashboard + alert + contact-point authorship back to each service team via the 5-CRD contract. Platform still owns infrastructure-level dashboards (Karpenter / Kyverno / ArgoCD / node health) and the Grafana stack itself; service teams own service SLOs + routing.
351
351
-[ ]`aegis_host_transient_loss_total`, `aegis_questions_detected_total`, `aegis_hints_emitted_total`, and other domain metrics emitted
> *"Close the auth gap between LAN demo and cold-apply cloud loop."*
356
+
357
+
Gated behind LDZ's `staging/auth/` Terraform apply (ldz ADR-026, cross-repo aegis-core #76 Partially Accepted 2026-04-23). Design recorded in [ADR-0034](docs/adr/0034-cloud-auth-cognito-jwt.md); Phase 2's `StaticJWTProvider` is preserved for integration-test scenarios (`DEPLOY_MODE=cloud-test`).
358
+
359
+
#### 4e-1 Gateway JWT middleware
360
+
361
+
-[ ]`gateway_go/internal/auth/oidc_provider.go` — `OIDCProvider` implementing the existing `auth.Provider` port (`gateway_go/internal/auth/auth.go:69-71`)
362
+
-[ ]`github.com/lestrrat-go/jwx/v2` dep added via `go get`; `go.mod` + `MODULE.bazel` + `gateway_go/BUILD.bazel` updated
-[ ] Structured error logging — failure category only (signature / expired / missing claim / JWKS fetch), never the token bytes
366
+
367
+
#### 4e-2 SPA OAuth scaffold
368
+
369
+
-[ ]`frontend_web/package.json` — `react-oidc-context` + `oidc-client-ts` deps (Cognito Hosted UI flow, provider-agnostic wrapper so future IdP swap is config-level)
370
+
-[ ]`frontend_web/src/main.tsx` — `<AuthProvider>` wrapper with Vite env-var config (`VITE_COGNITO_AUTHORITY` / `VITE_COGNITO_CLIENT_ID` / `VITE_COGNITO_REDIRECT_URI` / `VITE_COGNITO_LOGOUT_URI`)
371
+
-[ ]`frontend_web/src/routes/AuthCallback.tsx` — PKCE completion + navigate to post-login landing
372
+
-[ ]`frontend_web/src/lib/gateway-client.ts` — `Authorization: Bearer ${idToken}` injection on every outbound gRPC-Web call
373
+
-[ ]`frontend_web/src/lib/auth.ts` — decide (per ADR-0034 Open Question 1): extend with Cognito ID-token accessor, OR deprecate in favor of `useAuth()` hook + keep session-token storage for viewer-join
374
+
-[ ] Logout button → `user.signoutRedirect()` (Cognito global logout)
375
+
-[ ] Token storage: memory-only (`InMemoryWebStorage`) to mitigate XSS; silent-auth refresh on SPA reload
376
+
-[ ] Vitest for `AuthCallback` route + bearer injection
-[ ] Verify (and extend if needed) `gateway_go/internal/pipeline/` forwards `tenant_id` + `sub` via gRPC metadata to engine (per ADR-0022 §"Query path"); may be LAN-only today — per ADR-0034 Open Question 2
-[ ] Integration test: 2-tenant seed + cross-tenant query must return empty (structural + filter-level isolation both verified)
383
+
-[ ] Explicit failure-mode test: empty `custom:tenant_id` in JWT → gateway rejects with `Unauthenticated` at middleware, never reaches engine
384
+
385
+
#### 4e-4 Integration + E2E
386
+
387
+
-[ ] Dev User Pool registration on LDZ staging Cognito (coordinate via aegis-core #76 or open a new cross-repo issue when 4e-1 lands)
388
+
-[ ] Go integration test (gated on `AEGIS_COGNITO_*` env vars): real Cognito `AdminInitiateAuth` → token → `OIDCProvider.Authenticate` → expected `Principal` — nightly CI cadence, not PR-time
389
+
-[ ] (Optional) Playwright nightly spec: SPA → Cognito Hosted UI → CreateMeeting → transcript → hint render on staging (only valuable once cold-apply cloud loop is live)
0 commit comments