member_center/docs/MEMBER_UPGRADE_PLAN.md

791 lines
29 KiB
Markdown
Raw 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.

# 會員中心升級規劃
此文件整理會員中心的目標升級架構包含設定畫面、Email 驗證與忘記密碼、帳號分級與角色管理、會員主資料、訂閱管理,以及作為外部服務 Token / Auth 中心的整體模型。此文件以一次到位的最終設計為準,不再以過渡方案或分階段落地作為主軸。
## 目標
- 增加管理者可操作的系統設定畫面,涵蓋 SMTP、Send Engine 與 Auth 資源設定。
- 補齊 Email 驗證與忘記密碼的完整寄信流程。
- 建立帳號分級規則,明確區分 `superuser``admin` 與一般會員。
- 確立會員認證狀態模型,以「已認證 / 未認證」為會員狀態基礎。
- 第三方登入以 Google 為唯一支援 provider。
- 將「會員中心作為 Token / Auth 中心」的外部服務授權模型文件化,納入 Send Engine 與 File Access。
- 擴充會員個人資料、地址簿與會員端訂閱管理能力,並同步定義可供其他服務使用的 profile scopes。
- 補齊帳號生命週期、審計紀錄、rate limit 與 MFA 的基礎治理規則。
## 實作進度2026-04-17
已完成:
- 建立 `user_profiles` / `user_addresses` 實體、DbContext 映射與 EF migration
- `users` 新增治理欄位:
- `last_login_at`
- `last_seen_at`
- `disabled_at`
- `disabled_by`
- 註冊與 external login 建立新帳號時,會同步建立空白 profile row
- 新增 current-user profile API
- `GET /user/profile`
- `POST /user/profile`
- `GET /user/addresses`
- `POST /user/addresses`
- `DELETE /user/addresses/{id}`
- `GET /user/subscriptions`
- `POST /user/subscriptions/{id}/unsubscribe`
- 新增會員端 Web UI
- `/profile`
- `/profile/addresses`
- `/profile/subscriptions`
- 新增會員端直接退訂流程,不需再透過 email token
- `profile:*` scopes 已註冊進 OpenIddict
- `profile:basic.read`
- `profile:basic.write`
- `profile:addresses.read`
- `profile:addresses.write`
- `profile:subscriptions.read`
- `profile:subscriptions.write`
- API 已接上 profile scope policies並補 service API 的 by-email 端點:
- `GET /user/profile/by-email`
- `POST /user/profile/by-email`
- `GET /user/addresses/by-email`
- `POST /user/addresses/by-email`
- `DELETE /user/addresses/by-email/{id}`
- `GET /user/subscriptions/by-email`
- `POST /user/subscriptions/by-email/{id}/unsubscribe`
- token resource 映射已將 `profile:*` 納入 member center audience
- `/admin/security` 已擴充 SMTP 設定欄位:
- relay host / port
- TLS / SSL
- timeout
- username / password
- sender name / sender email
- `/admin/security` 已新增 SMTP 測試信功能,可輸入測試收件 Email 送出測試信
- SMTP 設定已收斂到 DB flags密碼欄位留白時保留既有值
- 已建立共用帳號寄信服務,用於 Email 驗證信與密碼重設信
- 已抽出共用帳號 Email 模板服務:
- 驗證信模板
- 密碼重設信模板
- 註冊流程已改為寄送 Email 驗證信
- forgot password 已改為寄送重設信,不再直接回傳 reset token
- 已補 resend verification
- Web`POST /account/resendverification`
- API`POST /auth/email/resend`
- `/admin/security` 已補 `PublicBaseUrl`,作為驗證信與重設信連結的基準 URL
- 已補 audit log
- `account.verification_email_sent`
- `account.password_reset_email_sent`
- `account.email_verified`
- `account.password_reset_completed`
- 已啟用 Identity lockout 基礎策略:
- `MaxFailedAccessAttempts = 5`
- `DefaultLockoutTimeSpan = 15 分鐘`
- `AllowedForNewUsers = true`
- 已落地公開入口 rate limit
- Weblogin / register / forgot password / resend verification
- APIregister / forgot password / resend verification
- Newsletter APIpublic subscribe / unsubscribe token
- password grant login 已改為走 `SignInManager.CheckPasswordSignInAsync(..., lockoutOnFailure: true)`,與 Web login 共用 lockout 行為
- 已完成 `superuser` / `admin` 權限模型第一版落地:
- `Admin` policy 已擴為接受 `admin``superuser`
- 新增 `Superuser` policy
- installer `init` / `add-superuser` 會建立或提升 `superuser`
- 舊指令別名 `add-admin` / `reset-admin-password` 仍可用
- 已新增管理後台帳號治理頁:
- `/admin/accounts`
- 支援查詢帳號、查看 email verified / role / disabled / last login
- 只有 `superuser` 可授予或移除 `admin`
- 只有 `superuser` 可停用或啟用帳號
- 已補帳號治理規則:
- `superuser` 帳號不可在管理 UI 中被降權、停用、刪除或強制變更密碼
- disabled 中的 `admin` 可保留 `admin` role
- `superuser` 可在管理 UI 強制重設非 `superuser` 帳號密碼
- 強制重設密碼後會更新 security stamp 並撤銷既有 OpenIddict authorization / token
- 已補帳號治理與生命週期 audit log
- `account.registered`
- `account.external_login_linked`
- `account.password_changed`
- `account.role_changed`
- `account.disabled`
- `account.enabled`
- `system.security_settings_updated`
- `system.security_test_email_sent`
- 已補登入治理:
- Web login / external login / password grant login 成功後更新 `last_login_at` / `last_seen_at`
- disabled 帳號無法透過 Web login、external login、password grant 取得登入
- Web cookie 與 API authenticated request 會檢查 disabled 狀態
- 已補 redirect 型登入 `web_login`
- OAuth client usage 新增 `web_login`
- 支援 Authorization Code + PKCE
- `/oauth/authorize` 未登入時會導向 Web login登入後回到原 authorize request
- `/oauth/token` 已支援 authorization code exchange
- API / Web 共用 DataProtection application name `MemberCenter`
- 支援 `Auth:WebLoginUrl``Auth:AllowedLoginReturnUrlPrefixes` 處理 Web / API 不同 origin 的 redirect login
進行中:
- profile / addresses / subscriptions 的畫面目前為最小可用版本,尚未優化樣式與完整驗證提示
待續作:
- resource registry 管理 UI
- Email 驗證信 / 重設信的正式模板與文案優化
- rate limit 與 lockout 規則補齊:
- one-click unsubscribe token 申請
- 服務型 token flow 與人類登入 flow 的完整差異化治理
- 更細的風控觀測與後台設定化
- audit log 與 rate limit 尚未全面覆蓋所有規劃入口與治理事件
- `superuser` / `admin` 第一版已完成,後續待細化:
- 更細權限切分
- 是否需要更多治理角色
- 管理後台帳號治理功能補強:
- 更完整排序 / 篩選進一步細化
- 更細的操作確認與保護規則擴充
## 現況盤點
### 已存在
- `MemberCenter.Web``MemberCenter.Api` 已有本地註冊、登入、忘記密碼、重設密碼、Email 驗證入口。
- `MemberCenter.Web/Controllers/AccountController.cs` 已有:
- `ForgotPassword`
- `ResetPassword`
- `VerifyEmail`
- `ExternalLogin` / `ExternalLoginCallback`
- `MemberCenter.Api/Controllers/AuthController.cs` 已有:
- `POST /auth/register`
- `POST /auth/password/forgot`
- `POST /auth/password/reset`
- `GET /auth/email/verify`
- Google external login 已在 `src/MemberCenter.Web/Program.cs` 接入,並已支援同 email auto-link。
- Installer 已可建立初始管理帳號,但角色模型仍需調整為文件定義的 `superuser`
- 管理後台已有 `/admin/security` 畫面,目前僅提供 token 時效設定:
- `AccessTokenMinutes`
- `RefreshTokenDays`
- `MemberCenter.Web` 已有 `/profile` 頁面,但目前僅顯示:
- Email
- Email 驗證狀態
- 建立時間
- `MemberCenter.Api` 已有 `GET /user/profile`,但目前只提供基礎欄位,不足以支撐其他服務查詢完整會員資料。
- `docs/DESIGN.md` / `docs/FLOWS.md` 已定義 File Access 流程:
- upload: `client_credentials` 取得 upload token 後由外部服務直連 file space
- download: 由業務服務驗權後,交由新 File Access 專案簽發或管理短效 delegated download token
- Auth resource registry 第一版已落地:
- 新增 `auth_resources``auth_resource_scopes``auth_client_usage_permissions`
- `TokenController` 已改由 scope 查 registry 決定 token audiences
- OAuth client usage 可使用的 scopes 已改由 DB permission matrix 決定
- 預設資源包含 `member_center_api``send_engine_api``file_access_api`
### 部分實作
- `SendEngine__BaseUrl``SendEngine__WebhookSecret` 仍停留在設定來源與 options binding尚未進入可編輯的管理畫面。
- Auth 資源 / audience / scope registry 已完成 DB 驅動第一版,仍缺管理 UI。
- 帳號治理後台、角色模型與 disabled account 規則已完成第一版,但仍缺進一步細化與保護規則。
- profile / addresses / subscriptions 畫面與驗證目前為最小可用版本,尚未完成 UI refinement。
### 待補項
- File Access 的 OAuth client usage、scope、audience 已落地delegated token 發放流程不放在 Member Center會在新 File Access 專案實作。
- Token resource / audience 已抽象為 registry後續需補 resource registry 管理畫面。
- Email 樣板正式文案與會員 / 後台 UI 細節仍待整理。
- rate limit 仍缺少 `one-click unsubscribe token` 與更細的風控觀測。
## 功能規劃
### 1. 系統設定畫面
狀態:`部分完成`
#### 1.1 主要新增項
- 新增獨立的「系統設定」或「整合設定」畫面。
- 第一批可編輯設定:
- SMTP Host
- SMTP Port
- SMTP Username
- SMTP Password
- SMTP From Name
- SMTP From Address
- SMTP Enable SSL / TLS
- `SendEngine__BaseUrl`
- `SendEngine__WebhookSecret`
目前進度:
- 已完成 SMTP 與 token lifetime 設定 UI沿用 `/admin/security`
- 已完成 SMTP 測試信
- 已完成 `PublicBaseUrl`
- `SendEngine__BaseUrl` / `SendEngine__WebhookSecret` 尚未進管理畫面
- Auth 資源設定暫未實作,仍保留待 audience/scope 抽象化後處理
#### 1.2 可一併納入的既有設定
- 目前 `/admin/security` 已有的 token 時效設定可整併進同一個設定體系:
- Access token 分鐘數
- Refresh token 天數
- `SendEngineWebhookOptions.SubscriptionEventsPath` 目前已有預設值 `/webhooks/subscriptions`,若未來有多環境或反向代理差異,可評估是否也納入設定畫面。
#### 1.3 暫不建議放入設定畫面的項目
- `ConnectionStrings__Default`
- `Auth__Issuer`
- `PathBase`
- Google OAuth Client Secret
以上屬部署層級或高風險設定,建議仍以環境變數或部署設定管理,不在一般管理 UI 直接編輯。
#### 1.4 設定保存策略
- 所有可運行期調整的設定統一收斂到 DB例如沿用 `system_flags` 或新增專用設定表。
- 敏感值至少需要:
- UI 遮罩顯示
- 審計紀錄
- 更新權限限制
- Secret 類設定需明確區分:
- 可明文顯示的設定
- 僅能覆寫、不可回顯的 secret 類設定
### 2. Email 驗證與忘記密碼
狀態:`核心完成,文案待續作`
#### 2.1 目標狀態
- 註冊後自動寄送 Email 驗證信。
- 使用者可重新發送驗證信。
- 忘記密碼送出後寄送 reset password email。
- Web UI 與 API 共享同一套 token 與寄信服務邏輯。
#### 2.2 目前已有的基礎
- Identity token provider 已可產生:
- email confirmation token
- password reset token
- Web 與 API 已有 verify/reset endpoint。
- View 已存在:
- `ForgotPassword`
- `ResetPassword`
- `VerifyEmailResult`
#### 2.3 待補實作
- Email 樣板正式文案整理:
- 驗證信
- 忘記密碼信
- 更完整的產品提示與畫面細節整理
#### 2.4 安全與產品規則
- 忘記密碼 API 與 UI 都應避免暴露帳號是否存在。
- Reset token 與 verify token 的 URL 應統一由設定的 public base URL 組出。
- 驗證信與重設密碼信都應記錄 audit log。
- 會員未驗證時是否允許登入與可操作範圍,需在實作時明確固定為單一規則,不保留模糊狀態。
### 3. 帳號分級與角色管理
狀態:`第一版完成,細化待續作`
#### 3.1 角色模型
- `superuser`
- 只能透過 installer 建立或提升。
- 可管理所有帳號角色。
- 可授予或移除 `admin`
- `admin`
- 必須先以一般會員身分註冊。
- 再由 `superuser` 手動授權。
- 不可自行提升其他帳號為 `admin`,除非後續明確擴權。
- `member`
- 預設註冊身分。
- 依 Email 驗證狀態再分為:
- `unverified`
- `verified`
#### 3.2 會員分級原則
- 會員狀態以「已認證 / 未認證」為基礎。
- 若未來擴充,可在不破壞既有角色模型下新增:
- VIP / 付費會員
- 停權 / 凍結
- 企業帳號等級
#### 3.3 對現行實作的調整方向
- 已完成:
- Installer 建立的高權限帳號改為 `superuser`
- 管理後台中的角色治理操作為 `superuser` 專屬
- 已新增 `/admin/accounts`,可查詢帳號、查看驗證狀態、指派或移除 `admin`、顯示 `superuser`
- 已補搜尋與篩選
- 待續作:
- 更細的角色切分與保護規則
#### 3.4 權限規則
- 只有 `superuser` 可變更角色。
- `admin` 可進入既有管理後台,但不可升降他人權限。
- 一般會員不可看見 `/admin/*`
- 未來若導入更多後台功能,建議區分:
- 後台使用權限
- 帳號治理權限
### 3.5 帳號生命週期
狀態:`部分完成`
- 帳號狀態至少包含:
- `active`
- `disabled`
- `locked`
- Email 驗證狀態獨立於帳號狀態,維持:
- `unverified`
- `verified`
- Email 為對外主識別 key不允許修改。
- 若使用者要更換 email採重新註冊新帳號不提供原帳號改 email 流程。
- 後台需保留帳號停用能力;停用後不得再登入與取得新 token。
- 建議記錄帳號治理欄位:
- `last_login_at`
- `last_seen_at`
- `disabled_at`
- `disabled_by`
- superuser 重設他人密碼、停用帳號、解除停用等治理規則,先記入規劃,細節待後續檢討。
目前進度:
- 已完成停用 / 啟用帳號
- 已完成 disabled 帳號不可登入與不可取得新 token
- 已完成 `last_login_at` / `last_seen_at` / `disabled_at` / `disabled_by`
- 已完成 superuser 重設非 superuser 帳號密碼,並強制讓既有 session / refresh token 失效
- superuser 本身的密碼治理仍限定走 installer
### 4. 第三方登入
狀態:`已完成本期範圍`
- 只支援 Google。
- 目前 Google external login 已存在,後續只補齊與整體權限模型的一致性。
- 若 Google 回傳 email 已驗證,可直接把會員標記為 `verified`;目前 `AccountProvisioningService` 已有依 external login provider 回填 `EmailConfirmed` 的基礎邏輯。
### 5. 會員基本資料與地址簿
狀態:`核心完成UI refinement 待續作`
#### 5.1 會員基本資料
- 會員可自行維護的基本資料欄位包含:
- `last_name`
- `first_name`
- `nick_name`
- `mobile_phone`
- `landline_phone`
- `date_of_birth`
- `gender`
- 公司名稱
- 部門
- 職稱
- 公司電話
- 統一編號
- 發票抬頭
- `remark`
- Email 為固定欄位,不提供變更流程;如需更換 email採重新註冊。
- Email 同時作為對外 API 的主 key。
建議必填欄位:
- `last_name`
- `first_name`
建議選填欄位:
- `nick_name`
- `mobile_phone`
- `landline_phone`
- `date_of_birth`
- `gender`
- `company_name`
- `department`
- `job_title`
- `company_phone`
- `tax_id`
- `invoice_title`
- `remark`
建議 enum
- `gender`
- `male`
- `female`
- `other`
- `unspecified`
欄位驗證建議:
- `first_name``last_name`
- 必填
- 長度 `1-100`
- `nick_name`
- 長度 `0-100`
- `mobile_phone``landline_phone``company_phone`
- 儲存標準化字串
- 盡量接近 E.164,市話可接受本地格式輸入後正規化
- `company_name``department``job_title`
- 長度 `0-200`
- `tax_id`
- 長度 `0-32`
- 驗證以台灣統編規則為主,但 schema 保留國際延展空間
- `invoice_title`
- 長度 `0-200`
- `remark`
- 長度 `0-1000`
- `date_of_birth`
- 使用 `date` 型別,不使用 datetime
資料使用原則:
- `first_name + last_name` 為正式姓名來源
- `nick_name` 為顯示用途,不作為唯一鍵
- `mobile_phone` 優先於 `landline_phone` 作為主要聯絡電話
目前進度:
- 上述欄位與資料模型、API、Web profile 編輯已完成第一版
- 欄位驗證與畫面體驗仍待整理
#### 5.2 地址簿 / 收貨地址清單
- 新增會員地址簿概念,支援一位會員維護多筆地址。
- 地址欄位包含:
- `label`
- 收件人姓名
- 收件人電話
- `country_code`
- `postal_code`
- `state_region`
- `city`
- `district`
- `address_line1`
- `address_line2`
- 公司名稱(可選)
- 是否預設地址
- 地址用途
- `address_meta_json`
- 地址用途建議先預留列舉:
- `shipping`
- `billing`
- `both`
- 資料格式規則:
- 以台灣使用情境為主
- 國碼、電話、郵遞區號、國家碼等欄位盡量符合國際格式
- 統編以台灣統編規則為主
- 地址採「結構化欄位 + `address_meta_json` 補充資料」混合設計,不採純 JSON
- 刪除規則:
- 若會員只剩最後一筆地址,不允許刪除
- 若刪除的是預設地址,系統需自動補選新的預設地址
建議必填欄位:
- `user_id`
- `label`
- `recipient_name`
- `recipient_phone`
- `country_code`
- `address_line1`
- `usage`
- `is_default`
建議選填欄位:
- `postal_code`
- `state_region`
- `city`
- `district`
- `address_line2`
- `company_name`
- `address_meta_json`
建議 enum
- `usage`
- `shipping`
- `billing`
- `both`
欄位驗證建議:
- `label`
- 長度 `1-100`
- `recipient_name`
- 必填
- 長度 `1-100`
- `recipient_phone`
- 必填
- 儲存標準化字串
- `country_code`
- 必填
- 使用 ISO 3166-1 alpha-2
- 固定 2 碼大寫
- `postal_code`
- 長度 `0-20`
- `state_region``city``district`
- 長度各 `0-100`
- `address_line1`
- 必填
- 長度 `1-255`
- `address_line2`
- 長度 `0-255`
- `company_name`
- 長度 `0-200`
- `address_meta_json`
- 可為 `null`
資料規則:
- 同一個 `user_id + usage` 最好限制只能有一筆 `is_default = true`
- `label` 可為自由文字,不先做 enum
- 地址至少保留一筆,因此最後一筆不可刪除
目前進度:
- 地址簿資料模型、API、Web UI 已完成第一版
- `最後一筆不可刪除` 已實作
- 預設地址切換與資料結構已完成
- 畫面提示與驗證細節仍待整理
#### 5.3 資料治理原則
- 會員基本資料與地址簿屬於會員中心主資料,可供其他服務查詢,但寫入權限需嚴格控管。
- 會員本人可編輯自己的基本資料與地址簿。
- 其他服務預設只有讀取權限。
- 若其他服務需要代會員寫入,必須有額外 scope 與審計規則。
### 6. 會員資料 API 與 Auth Scope 規範
狀態:`scope 已落地,資源抽象化待續作`
#### 6.1 規劃目的
- 讓其他服務可透過 API 取得會員中心的基本資料與地址資料。
- 在一開始就把 scope 邊界定清楚,避免未來 profile API 無限制外放。
#### 6.2 建議 scopes
- `profile:basic.read`
- 讀取會員基本資料
- `profile:basic.write`
- 更新會員基本資料
- `profile:addresses.read`
- 讀取會員地址簿
- `profile:addresses.write`
- 新增、修改、刪除會員地址簿
- `profile:subscriptions.read`
- 讀取會員已訂閱電子報清單
- `profile:subscriptions.write`
- 透過已登入會員介面取消訂閱或調整會員自己的訂閱狀態
目前進度:
- `profile:*` scopes 已註冊並接上 policy
- current-user 與 by-email service API 已完成第一版
- audience / resource registry DB 驅動第一版已完成
#### 6.3 API 邊界建議
- 其他服務 API
- 目前規劃以 service API 為主
- 只要 Auth 設定有授與對應 scope該服務即可存取對應資料
- 讀寫能力完全由 scope 控制
- 預設以最小權限授權,不因 client 類型自動放寬資料邊界
- 會員本人 UI / API
- 可讀寫自己的 profile 與地址
- 可查看自己的訂閱清單
- 可對自己的訂閱直接退訂,不需再次 email token 確認
- 管理後台:
- 可查詢會員資料,但是否可編輯應另行定義,不與一般會員本人編輯權混用
#### 6.4 與 OIDC 標準 scope 的關係
- 目前 `openid email profile` 中的 `profile` scope 太寬泛,不足以承載業務上需要的個資與地址簿授權控制。
- 建議保留 OIDC `profile` 作為基本 claims 用途,但業務資料改由自訂 scope 控制。
### 7. 會員端訂閱管理(我的訂閱)
狀態:`核心完成newsletter 補強待續作`
#### 7.1 目標
- 會員登入後,可集中查看自己目前已訂閱的電子報清單。
- 會員可直接從此介面取消訂閱,不需要再次透過 email token 驗證。
- 此流程與 email 內的一鍵退訂屬不同入口:
- email 退訂:適用未登入或直接從信件操作
- 會員中心退訂:適用已登入且已確認是本人
#### 7.2 UI 能力
- 顯示已訂閱清單:
- 所屬 tenant / 站台
- 電子報清單名稱
- 訂閱狀態
- 訂閱建立時間
- 最後更新時間
- 可執行:
- 直接取消訂閱
- 未來可擴充為偏好調整或重新訂閱
目前進度:
- 我的訂閱頁與 current-user 訂閱 API 已完成
- 會員登入後可直接退訂,不需再次 email token
- 進一步的 newsletter UI / 行為補強本輪刻意先跳過
#### 7.3 流程規則
- 已登入會員對自己的訂閱執行退訂時,不需 email token 二次確認。
- 系統仍需:
- 驗證 subscription 確實屬於目前登入會員
- 寫入 audit log
- 發送 `subscription.unsubscribed` 事件
- 若該 email 已在黑名單中,行為需與既有退訂規則一致。
- 即使已綁定 `user_id`,仍保留既有以 `list_id + email` 進行訂閱 / 退訂的 public 流程。
- 會員可重新訂閱已退訂的電子報,規則與既有 public subscribe 流程保持一致。
#### 7.4 與現行訂閱 API 的差異
- 現行 `/newsletter/preferences` 偏向以 `list_id + email` 操作,主要照顧跨站 API 邊界。
- 新的「我的訂閱」介面應改以登入者身份為主體,不再要求使用者自行輸入 email。
- 建議新增一組以 current user 為主體的 API而不是把既有未登入流程硬改成同一支。
### 7.5 訂閱識別原則
- Email 是訂閱領域的主 key。
- `user_id` 為已註冊會員與訂閱資料的關聯鍵,不取代 email 在訂閱流程中的角色。
- 因此系統同時保留:
- public flow`list_id + email`
- member flow`current user`
- 兩種入口最終都回到同一組 subscription 資料。
### 8. 會員中心作為外部服務的 Token / Auth 中心
狀態:`Auth 基礎已完成,管理 UI 待補`
#### 8.1 共通模型
- Send Engine 與 File Access 本質上是同一套模型:
- 由 Member Center 簽發 access token
- 外部服務以 Member Center JWKS 驗簽
-`iss/aud/exp/scope/tenant_id` 做授權與租戶邊界控制
- 差異只在資源類型不同:
- Send Engine 偏向 service-to-service API 呼叫
- File Access 除了 upload 的 S2S token 外download 還需要 delegated short-lived token
#### 8.2 File Access 納入規劃
- 新增 OAuth client usage建議至少區分
- `file_api`
- 或更明確拆成 `file_upload_api` / `file_download_delegate`
- 新增 scopes
- `files:upload.write`
- `files:download.read`
- `files:download.delegate`
- `files:delete`
- `files:metadata.read`
- 新增 audience
- `file_access_api`
- File Access 新專案需實作 delegated token 規則:
- 下載 token 必須短效
- 必須綁定 `tenant_id`
- 必須綁定 `file_id``object_key`
- 必須綁定 `method`
- 不可直接重用一般 S2S access token 給 client
#### 8.3 目標設計
- Auth 資源設定採 resource registry不再以一組一組 `Auth__XAudience` 擴充。
- 每個外部資源服務都以統一結構註冊:
- resource name
- audience
- scopes
- client usages
- 是否需要 `tenant_id`
- 是否允許 delegated token供 File Access 判斷,不代表 Member Center 負責簽發檔案下載 token
- 新增資源服務時,只擴充 registry 與授權規則,不再修改硬編碼 audience 分支。
- 所有外部資料存取均以 scope 作為唯一授權依據。
#### 8.4 授權模型
- OAuth client usage 與 resource 綁定:
- `tenant_api` -> `member_center_api`
- `send_api` -> `send_engine_api`
- `file_api` -> `file_access_api`
- scope 用於細粒度權限控制。
- `TokenController` 依 scope 與 usage 對照 resource registry 計算 `resources/audiences`
- delegated download token issuing 屬於新 File Access 專案責任Member Center 只負責發出可呼叫 File Access 的 OAuth access token。
目前進度:
- Send Engine、Member Center profile/newsletter scopes、File Access scopes 已進 registry
- `TokenController` 已以 registry 解析 audiences
- OAuth client usage-scope matrix 已以 `auth_client_usage_permissions` 驅動
- File Access delegated token issuing、流程測試與 sample code 會在新 File Access 專案實作
### 9. 審計紀錄
狀態:`大致完成,少數治理事件待續作`
- 下列事件必須寫入 audit log
- 註冊
- Email 驗證成功
- 忘記密碼申請
- 密碼重設成功
- 已登入修改密碼
- Google external login 綁定
- 角色變更
- 帳號停用 / 啟用
- profile 修改
- 地址簿新增 / 編輯 / 刪除 / 預設地址切換
- 會員端直接退訂
- 系統設定修改
- OAuth client 建立
- OAuth client secret 旋轉
- OAuth client secret 顯示一次、旋轉與治理能力視為既有基線;細節之後再檢討。
目前進度:
- 帳號寄信、驗證、重設密碼、修改密碼、註冊、external login 綁定、角色變更、帳號停用 / 啟用、profile、地址、會員端退訂、系統設定修改均已有實作
- OAuth client 建立與 secret 旋轉等治理細節仍待續作
### 10. Rate Limit 與防濫用
狀態:`部分完成`
- 下列入口必須有 rate limit
- login
- forgot password
- resend verification
- register
- public subscribe
- unsubscribe token 申請
- one-click unsubscribe token 申請
- lockout 與 rate limit 需能區分:
- 人類使用者登入
- service API token 申請
目前進度:
- 已完成 login / forgot password / resend verification / register / public subscribe / unsubscribe token 申請
- `one-click unsubscribe token` 申請仍待補
- 人類登入 flow 已有 lockoutservice API token flow 與更細觀測仍待續作
### 11. MFA 與非本期項目
狀態:`待續作`
- 在 Email 寄送能力完成後,可規劃 Email-based MFA / challenge 驗證。
- 其餘先明確列為未來可補項:
- consent / terms acceptance
- 資料匯出 / 刪除
- login history / device management
- 通知偏好中心
## 一次到位的實作範圍
1. 設定中心:
- SMTP
- Send Engine
- Auth 資源設定
- token lifetime
2. Identity 與通知:
- Email 驗證寄信
- 忘記密碼寄信
- 重送驗證信
3. 會員主資料:
- 基本資料
- 地址簿
- Profile UI / API
4. 訂閱管理:
- 我的訂閱頁
- current-user 型訂閱 API
- 直接退訂流程
5. 權限與角色:
- `superuser`
- `admin`
- `member` + verified / unverified
- 帳號管理與角色指派
6. Auth 中心:
- resource registry
- profile scopes
- file access scopes
- delegated token
7. 安全治理:
- audit log
- rate limit
- MFA 預留
8. 文件、測試與 installer 同步調整
目前整體狀態:
- `1`:部分完成
- `2`:核心完成,文案待續作
- `3`:核心完成
- `4`:核心完成,但本輪不再補強 newsletter refinement
- `5`:第一版完成
- `6`:先跳過 audience / scope 抽象化
- `7`:大致完成,仍有少量補強
- `8`:部分完成
## 影響範圍
### Web
- 新增設定畫面與表單。
- 新增帳號管理畫面。
- 調整註冊成功後導引與驗證提醒文案。
- 新增會員 profile 編輯頁、地址簿頁與我的訂閱頁。
### API
- 若設定畫面走 API需新增設定讀寫端點。
- 若帳號管理後台走 API需新增角色管理與帳號查詢端點。
- File Access 的 resource / audience mapping 與 file scopes 已完成;流程測試與 sample code 會在新 File Access 專案實作。
- 需新增 current-user 型 profile / addresses / subscriptions API 與對應 scopes。
### Infrastructure
- 新增 SMTP sender 與設定存取服務。
- 調整帳號 provisioning 與角色管理服務。
- token resource resolver 已抽到 registry serviceFile Access delegated token issuer 不屬於 Member Center。
- 新增會員基本資料與地址簿的資料模型與服務層。
### Installer
- 建立或提升 `superuser`
- 視命名策略決定是否保留 `add-admin`,或改名為更貼近語意的指令。
## 文件與實作同步原則
- 文件中的「已存在」代表已有 controller / route / view / 基礎邏輯,不代表整體功能已完成產品化。
- 本規劃完成後,應同步回寫:
- `docs/UI.md`
- `docs/FLOWS.md`
- `docs/INSTALL.md`
- `README.md`