Skip to content

Commit b3ff7b0

Browse files
BinHsuclaude
andcommitted
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>
1 parent 6a2674a commit b3ff7b0

2 files changed

Lines changed: 283 additions & 0 deletions

File tree

ROADMAP.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,44 @@ Gated by 3b — prompter display needs real transcript data; corpus selector nee
350350
- [ ] ~~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.
351351
- [ ] `aegis_host_transient_loss_total`, `aegis_questions_detected_total`, `aegis_hints_emitted_total`, and other domain metrics emitted
352352

353+
### Phase 4e: Cloud-mode Authentication (Cognito JWT consumption)
354+
355+
> *"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
363+
- [ ] `cmd/gateway/main.go` factory extended — `DEPLOY_MODE` switch: `local``NoOpProvider`, `cloud``OIDCProvider`, `cloud-test``StaticJWTProvider` (preserves pre-Cognito integration-test scaffold)
364+
- [ ] Unit tests (`oidc_provider_test.go`) — `httptest` mock JWKS server + synthesised JWTs covering: happy path, expired token, wrong audience, wrong issuer, missing `custom:tenant_id`, signature mismatch, JWKS fetch failure + refresh retry, JWKS key rotation mid-flight
365+
- [ ] 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
377+
378+
#### 4e-3 `custom:tenant_id` propagation verification
379+
380+
- [ ] 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
381+
- [ ] Engine-side: `AuthContext` interceptor reads metadata → populates `session.TenantID` → Qdrant search honours ADR-0022 collection naming (`aegis_<tenant_id>_<corpus>`) + payload filter
382+
- [ ] 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)
390+
353391
---
354392

355393
## Phase 5: Hardening & Compliance

0 commit comments

Comments
 (0)