# 會員中心升級規劃 此文件整理會員中心的目標升級架構,包含設定畫面、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: - Web:login / register / forgot password / resend verification - API:register / forgot password / resend verification - Newsletter API:public 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 已有 lockout;service 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 service;File Access delegated token issuer 不屬於 Member Center。 - 新增會員基本資料與地址簿的資料模型與服務層。 ### Installer - 建立或提升 `superuser`。 - 視命名策略決定是否保留 `add-admin`,或改名為更貼近語意的指令。 ## 文件與實作同步原則 - 文件中的「已存在」代表已有 controller / route / view / 基礎邏輯,不代表整體功能已完成產品化。 - 本規劃完成後,應同步回寫: - `docs/UI.md` - `docs/FLOWS.md` - `docs/INSTALL.md` - `README.md`