# Phase 0.6 OIDC 接入 — 交接摘要 > 日期:2026-04-26 > > 範圍:visionA-backend StaticAuthProvider → Innovedus Member Center OIDC(BFF Pattern + Authorization Code + PKCE) > > 上位文件:`.autoflow/04-architecture/oidc-tdd.md`、`.autoflow/04-architecture/adr/adr-010-oidc-bff.md`、`.autoflow/04-architecture/adr/adr-011-supersede-static-auth.md` > > 整體交付:見 `.autoflow/07-delivery/project-summary.md` --- ## 1. Phase 0.6 完成項目 ### Backend(OB1-OB6)✅ - **OB1** `internal/oidc/`:coreos/go-oidc 包裝,discovery + JWKS + token exchange + id_token 驗簽(24 tests / 93.4% 覆蓋) - **OB2** `internal/usersession/`:in-memory session store + cookie HMAC 簽章(24 tests) - **OB3+OB4** `internal/api/middleware/auth.go` + 4 個 OIDC handler(`/api/auth/login` / `/callback` / `/logout` / `/me`)(14 tests) - **OB5** 完全移除 `StaticAuthProvider`、AuthenticatedClient fixture 統一(17 packages 全綠 / oidc_e2e build tag 移除) - **OB6** ADR-011 推翻 ADR-005 Auth 部分(DB 部分仍有效) ### Frontend(OF1-OF2)✅ - **OF1** login 頁改 OIDC redirect 按鈕(11 tests + i18n) - **OF2** API client `credentials: 'include'`、auth-store 拔 localStorage、改 cookie session(107 tests 綠) ### Frontend(OF3-OF4)⏳ - **OF3** register 頁移除、account 接 `/api/auth/me`(待補) - **OF4** i18n 補齊(待補) ### DevOps(OD1)✅ - 5 service docker-compose(postgres / member-center / member-center-web / member-center-init / visiona-api / visiona-proxy)全 healthy - `docs/DEV-SETUP.md` 完整 setup + 故障排除(含 §7.6 OIDC flow 階段問題) ### DevOps(OD2)⏳ - `make dev-with-mc` Makefile target + `.env.example` 整理(待補;MC bug 修復後加 seed script) ### Testing(OT1)✅ - `internal/oidc/oidctest/`:fake OIDC server(自簽 JWKS / 模擬 authorize / token endpoints) - `internal/api/handlers/oidc_e2e_test.go`:6 個 e2e cases(含 `PairingTokenBindsToOIDCUser`,驗 OIDC sub 真的有貫通到 PairingStore) - 17 packages 全綠 / build tag 已拿掉(合進主測試) ### Testing(OT2)✅(範圍調整) - 因 MC 兩個 bug 阻擋自動 e2e(password grant 缺 sub claim、admin API 不接受 client_secret),原訂「真 MC e2e」改為: - `docs/SMOKE-TEST.md`:完整 7 階段手動煙測 checklist - `docs/DEV-SETUP.md` §7.6 補 OIDC flow 故障排除對照表 - 真 MC 自動 e2e 列入 Phase 1 TODO(待 MC 修 bug) --- ## 2. 跑通的 Demo ### Demo A:OIDC Flow(手動,需先依 `docs/DEV-SETUP.md` §5 註冊 OAuth client) **端到端使用者旅程**(對應 `docs/SMOKE-TEST.md` 階段 3-7): ``` [browser] http://localhost:3000/login ↓ 點「使用您的 Innovedus 帳號登入」 [backend] GET /api/auth/login ↓ 產 PKCE + state + nonce,存 pending session(10 分鐘 TTL) ↓ Set-Cookie: visiona_pending_sid ↓ 302 to MC /oauth/authorize?response_type=code&...&code_challenge=... [MC] http://localhost:5050/oauth/authorize ↓ 顯示登入頁 → demo@visiona.local / Demo12345! ↓ 同意授權 ↓ 302 back to http://localhost:3721/api/auth/callback?code=...&state=... [backend] GET /api/auth/callback ↓ 驗 state(CSRF) ↓ POST MC /oauth/token (code + verifier + client_secret) ↓ 驗 id_token(iss / aud / exp / nonce / JWKS 簽章) ↓ 建 visionA session(user_id = OIDC sub, 7d / 24h idle) ↓ Set-Cookie: visiona_session ↓ 302 to http://localhost:3000{return_to} [browser] dashboard 顯示已登入 demo user ↓ 後續 API request 自動帶 visiona_session cookie [backend] /api/devices /api/models /api/auth/me 全帶 cookie 認證 ``` ### Demo B:Pairing Token 綁 OIDC user(關鍵承諾驗證) **目的**:驗證取代 StaticAuth 後,pairing token 不再綁 `demo-user` 而是真實 OIDC sub。 ``` [browser] /devices/pair → 點「產生 Pairing Token」 [backend] POST /api/pairing/token (cookie: visiona_session) ↓ middleware 解 cookie → UserContext{UserID: } ↓ PairingStore.Create(userID=, token=vAc_xxx) ↓ log: "PairingStore Create userID=" [browser] 拿到 token vAc_xxx ↓ 餵給 local-tool agent [agent] 連 visiona-proxy → 帶 pairing token → 換 session token [backend] agent 連入 → 後端 binding user_id 是 OIDC sub(與 step 2 一致) ``` ### Demo C:fake OIDC e2e(自動化) ``` go test ./internal/api/handlers/... -run TestOIDCE2E ``` 跑完含 6 個 case: - LoginRedirectsToProvider - CallbackCreatesSession - StateMismatchRejected - NonceMismatchRejected - ExpiredTokenRejected - **PairingTokenBindsToOIDCUser**(最關鍵) 17 packages 全綠 / build tag 已合進主測試。 --- ## 3. 手動 Demo Flow 完整步驟見 **`docs/SMOKE-TEST.md`**,7 階段 / 預估 30 分鐘: | 階段 | 內容 | 時間 | |------|------|------| | 1 | 基礎服務驗證(5 service + frontend healthy)| 5 min | | 2 | MC 設定(建 tenant + OAuth client + demo user)| 10 min | | 3 | 完整 Login Flow(redirect chain 走完)| 5 min | | 4 | API 帶 Cookie 驗證(/me + /devices + /models)| 3 min | | 5 | Pairing Token 綁 OIDC user(**關鍵承諾**)| 5 min | | 6 | 登出 + 重登 | 3 min | | 7 | 跨頁 Refresh / 持久化 | 2 min | 每個階段都有對應的「故障排除 #N」可查;卡住先看 `docs/SMOKE-TEST.md` §故障排除 + `docs/DEV-SETUP.md` §7。 --- ## 4. 已知 Limitation | # | Limitation | 影響範圍 | |---|-----------|---------| | 1 | MC admin API 不能 seed OAuth client(password grant + client_secret 兩個 bug)| §2 OAuth client 註冊必須手動走 Web UI;自動 e2e blocked | | 2 | MC 只支援 `usage=webhook_outbound` 帶 redirect_uris | 雛形借用,命名語意不對 | | 3 | visionA-backend in-memory session store 重啟即消失 | 所有 user 重登 | | 4 | 沒有 RP-initiated logout | 登出 visionA 不會把 MC session 也登出 | | 5 | 沒有 refresh token rotation | 24h idle / 7d absolute 後必須重登 | | 6 | host visiona-local(local-tool)佔 3721 與 docker compose 衝突 | 兩個只能擇一跑 | --- ## 5. Phase 1 TODO ### MC team(unblock visionA 自動化) - [ ] **MC-1** 修 password grant Identity user 缺 sub claim → 500 bug - [ ] **MC-2** admin API 接受 + 回傳 client_secret - [ ] **MC-3** 新增 `usage=web_app` OAuth client 類型 ### visionA Phase 1 - [ ] **OD2** `make dev-with-mc` + `make dev-seed` script(依賴 MC-1 + MC-2) - [ ] **OT2 補完** 真 MC 自動 e2e(用 testcontainers 起真 MC,把 SMOKE-TEST §3-§7 全自動) - [ ] **OF3 OF4** register 頁移除 + account 改造 + i18n 補齊 - [ ] **OB-Phase1-1** in-memory session store → Redis - [ ] **OB-Phase1-2** RP-initiated logout(OIDC end_session_endpoint) - [ ] **OB-Phase1-3** Refresh token rotation(依賴 MC 上 refresh token 支援) - [ ] **OB-Phase1-4** Member Center webhook(user 刪除 / 停用通知) --- ## 6. 相關文件索引 | 主題 | 文件 | |------|------| | 架構決策 | `.autoflow/04-architecture/adr/adr-010-oidc-bff.md`、`adr/adr-011-supersede-static-auth.md` | | TDD(含時序圖、模組設計、env、安全考量)| `.autoflow/04-architecture/oidc-tdd.md` | | Dev 環境 setup | `docs/DEV-SETUP.md` | | 手動煙測 | `docs/SMOKE-TEST.md` | | 整體 visionA 交付 | `.autoflow/07-delivery/project-summary.md` | --- ## 7. 驗收狀態 - [x] 端到端 redirect chain 通(fake OIDC e2e 自動驗) - [x] Pairing token 綁 OIDC sub(OT1 PairingTokenBindsToOIDCUser ✅) - [x] StaticAuthProvider 完全移除(OB5 ✅) - [x] Frontend 完全不接觸 token(OF1 + OF2 ✅) - [x] docker-compose 一鍵起 5 service(OD1 ✅) - [x] 手動煙測 checklist 完整(OT2 ✅,本文件對應) - [ ] OF3 + OF4 完成(待補;不阻擋 Phase 0.6 驗收,列入 Phase 1) - [ ] OD2 Makefile + seed script(依賴 MC bug 修復;列入 Phase 1) - [ ] 真 MC 自動 e2e(依賴 MC bug 修復;列入 Phase 1)