visionA/docs/autoflow/04-architecture/adr/adr-011-supersede-adr-005.md
jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類
共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git),
讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等
per-branch 筆記。

- 02-prd/        21 個檔(PRD、features、market-analysis 等)
- 03-design/     18 個檔(design-spec、wireframes、flows 等)
- 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等)
- 07-delivery/   3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup)

合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv,
但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
2026-05-04 16:55:55 +08:00

146 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-011推翻 ADR-005 — 雛形階段升級接 Innovedus Member Center
## 狀態
Accepted — 2026-04-26
## 推翻
[ADR-005](./adr-005-no-db-auth-in-prototype.md) — Auth 部分DB 部分仍有效)
## 背景 (Context)
ADR-0052026-04-21在 Phase 0 雛形階段決定**不接 auth**
-`StaticAuthProvider` 任何帳密都通過、永遠回 `demo-user`
- `StaticAuthService` middleware 永遠注入 demo-user UserContext
- 雛形 PRD / TDD 中所有 user-bound 資源Device / Model / PairingToken都綁到 demo-user
當時的決策邏輯:
- 雛形階段目標是驗證「雲端端對端連通」技術可行性
- 把時間花在 Auth、user 系統、DB schema 上會延誤核心驗證
- 介面(`AuthProvider` / `AuthService`)已切乾淨,未來換實作零業務邏輯改動
到了 Phase 0.62026-04-26情況變了
- 雛形已交付Phase 0 + Phase 0.5 全綠),核心架構通過驗證
- 需要進入 Phase 1多用戶、上線給 FAE 試用)— 沒真實 user 寸步難行
- 同期間 Innovedus 集團另一條線 **Member Center 已可用**C# .NET Core + OpenIddict + PostgreSQL70% 完成度OAuth Authorization Code + PKCE + JWKS 都能跑)
- 跨產品 SSO 是 Innovedus 集團方向visionA / kneron_model_converter / 未來其他產品線)
繼續用 StaticAuth 的代價:
- 無法做多用戶測試(內部 FAE 試用無法區分使用者)
- 無法把雛形交給內部使用者(資料會混在一起)
- 之後要接 OIDC 還是要做 → 不如現在做完,後面少一輪改動
## 決策 (Decision)
**推翻 ADR-005 的 Auth 部分**Phase 0.6 起 visionA-backend 的唯一認證路徑改為 OIDC接 Innovedus Member Center。
### 具體做法
1. **拔除 StaticAuthProvider / StaticAuthService**
- 刪除 `internal/auth/static.go` + `static_test.go`
- `internal/api/middleware.go``AuthMiddleware` 拿掉雙模式分支,只剩 OIDC
- `internal/api/api.go``Deps` 拿掉 `AuthService` / `AuthProvider` 欄位;`validate()` 強制 `OIDCProvider` + `SessionManager` 必填(缺則啟動 panic
- `cmd/api-server/main.go` 拿掉 `cfg.OIDC.Enabled` 旗標分支OIDC 變成必須路徑
2. **OIDC 實作**OB1-OB4 已完成;本 ADR 只記錄決策)
- 採 OAuth 2.0 Authorization Code + PKCE + BFF Pattern詳見 ADR-010
- visionA-backend 是 OIDC confidential client持有 client_secret
- frontend 完全不接觸 token只看到 `visiona_session` cookie
- In-memory session store重啟即消失雛形階段可接受
3. **保留 AuthProvider / AuthService interface**
- 雖然 Static 實作已刪interface 本身仍保留在 `internal/auth/auth.go`
- 給未來新增備援 providerPhase 1 backup local auth、service-to-service token使用
- 介面代表 contract — 提早設計、提早被驗證的介面比 ad-hoc 重新發明便宜
4. **Pairing Token 流程不動**ADR-005 的另一個面向)
- PairingStore / SessionTokenStore 仍是 in-memory + interface 抽象
- 唯一改變:`UserContext.UserID` 從固定的 `"demo-user"` 變成 OIDC `sub`UUID自然穿透到 `PairingStore.Create` 的 user_id 參數
- Agent 端不知道 user 是誰、不接 OIDC
5. **dev 環境策略**
- 不寫 mock OIDC server測試例外 — 見 `internal/oidctest`
- 開發者要登入就得起 Member Centerdocker-compose 一鍵化)
- 為了讓 demo-user 流程繼續可用Member Center 端會 seed `demo@visionA.local / demo123` 帳號
### 仍維持 ADR-005 規範的部分
- **不接真實 DB**Device / Model / Pairing 仍用 InMemoryRepository
- **Repository interface 抽象**Phase 1 換 PostgresRepository 仍然零業務邏輯改動
- **資料重啟會遺失**:雛形 process 重啟 → user session、device、model、pairing token 全消失
## 考慮過的替代方案
| 方案 | 評估 | 排除原因 |
|------|------|---------|
| **繼續用 StaticAuth 到 Phase 1** | 不用做事 | 無法多用戶測試FAE 試用會撞牆;之後還是要接 |
| **保留 dev fallback 切換** | dev 環境不需起 Member Center | 各環境行為分歧難排查;且 Member Center docker-compose 一鍵起,成本不高 |
| **接 Auth0 / Clerk / Cognito** | 現成 | 跨 Innovedus 產品 SSO 需企業方案vendor lock-in資料外流 |
| **自刻 email + password + JWT** | 完全掌控 | 要做密碼重設、2FA、暴力破解防禦維運成本太高 |
詳細替代方案分析見 [ADR-010](./adr-010-oidc-bff.md)。
## 後果 (Consequences)
### 正面影響
- **跨 Innovedus 產品 SSO**:使用者一組帳號用所有 Innovedus 產品線
- **真實 user binding for Pairing token**:多使用者上線時不再混淆
- **進 Phase 1 的前置就緒**FAE 內部試用、多用戶測試、上線都有 Auth 基礎
- **frontend 安全性提升**Token 在 backend不存 localStorage順便清掉 security.md §14.1 / §14.2 / §14.3 三筆雛形安全債)
- **demo-user 流程繼續可用**Member Center seed 同名帳號,雛形 demo 體驗不變
- **程式碼更乾淨**:拔除「雙模式 if/else」之後 auth middleware 邏輯線性、容易追蹤
### 負面影響(接受的取捨)
- **dev 環境多一個依賴**:起 visionA 要先起 Member Center + Postgresdocker-compose 緩解)
- **對 Member Center dev 環境穩定性有依賴**MC 掛掉 visionA 也無法登入
- 緩解docker-compose 固定版本Member Center 團隊維持基礎可用性
- **session 重啟即消失**in-memory store雛形 Phase 0.6 可接受,內部測試者
- Phase 1 換 Redis/DB同 interface
### 風險
| 風險 | 緩解 |
|------|------|
| Member Center API 改動 → visionA 跟著爆 | 用 OIDC 標準介面discovery + JWKS — Member Center 改實作不改介面就 OK |
| Member Center webhookuser 刪除 / 停用)未實作 | 雛形不接收Phase 1 補(記入 oidc-tdd.md TODO |
| OIDC client_secret 外洩 | env / Secrets Manager + 不 commit + log 不印 secret + 可隨時 rotate |
| 既有 Pairing flow 對 user_id 變動的破壞 | OB5 整合測試 `TestOIDCE2E_PairingTokenBindsToOIDCUser` + `TestOIDCE2E_MultiUserIsolation` 驗證 user binding 正確 |
## 合規性
- [x] Architect 確認
- [x] 使用者裁決 Q1OIDC + PKCE redirect— 見 ADR-010
- [x] 使用者裁決 Q4完全取代 StaticAuth不保留 dev fallback— 見 ADR-010
- [x] OB5 任務完成StaticAuth 已從 codebase 移除
- [x] OB5 任務完成:所有測試(含原 build-tag 隔離的 oidc_e2e_test.go皆通過
- [ ] Phase 1 Kick-off補 user 表 + 接 DB取代 in-memory session store
## 相關文件
- 上位:[ADR-005](./adr-005-no-db-auth-in-prototype.md)(被本 ADR 推翻 Auth 部分DB 部分仍有效)
- 同層:[ADR-010](./adr-010-oidc-bff.md)OIDC 接入策略 — 為什麼選 BFF + Authorization Code + PKCE + Member Center
- 詳細實作:[oidc-tdd.md](../oidc-tdd.md)Phase 0.6 OIDC TDD 增補)
- 安全:[security.md](../security.md) §2、§14前端安全債清單
## 後記2026-05-01
Phase 0.7 stage 部署時發現 Innovedus stage Member Center 配給 visionA 的 login OAuth client 是 **public PKCE-only**(無 `client_secret`),與本 ADR §32 中「visionA-backend 是 OIDC confidential client持有 client_secret」的假設不符。
具體狀況:
- Stage MC 端為 redirect flow 配的是 public client`b8093fea1a504a5d8f0e04bee9f78f2e`,無 secret
- 另配一組 confidential client`<see stage .env.stage>` + secret給未來 client_credentials grant 用,**login flow 不用此組**
- 表示 ADR-010 §「MC 強制 confidential」前提對 redirect flow 不再成立(對 server-to-server flow 仍成立)
處理:
- visionA-backend 擴充為兩種 mode 都支援ClientSecret 從必填變選填runtime 判斷走哪條路)
- 詳細決策見 [ADR-013](./adr-013-oidc-public-pkce-client.md)
- 本 ADR 的核心結論OIDC 取代 StaticAuth、保留 AuthProvider/AuthService interface、Pairing 流程不動、in-memory session**全部仍有效**,只是 client 配置層多了一個維度
## 版本記錄
| 日期 | 版本 | 變更 |
|------|------|------|
| 2026-04-26 | 1.0 | 初版 — OB5 完成後正式記錄推翻 ADR-005 Auth 部分 |
| 2026-05-01 | 1.1 | 新增「後記」段stage 部署發現 MC login client 為 public PKCE-only擴充由 ADR-013 處理 |