依 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)。
146 lines
8.3 KiB
Markdown
146 lines
8.3 KiB
Markdown
# 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-005(2026-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.6(2026-04-26),情況變了:
|
||
- 雛形已交付(Phase 0 + Phase 0.5 全綠),核心架構通過驗證
|
||
- 需要進入 Phase 1(多用戶、上線給 FAE 試用)— 沒真實 user 寸步難行
|
||
- 同期間 Innovedus 集團另一條線 **Member Center 已可用**(C# .NET Core + OpenIddict + PostgreSQL,70% 完成度,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`
|
||
- 給未來新增備援 provider(Phase 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 Center(docker-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 + Postgres(docker-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 webhook(user 刪除 / 停用)未實作 | 雛形不接收;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] 使用者裁決 Q1(OIDC + 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 處理 |
|