member_center/docs/FLOWS.md
2026-07-07 13:27:49 +09:00

159 lines
9.2 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.

# Flows
流程以「API 自建 UI」與「會員中心統一 UI」兩種模式描述。
## F-01 註冊會員
- [API] 站點送出 `POST /auth/register`
- [API] 會員中心建立 user、寄送驗證信
- [UI] 導向會員中心註冊頁完成註冊
- [UI] 會員中心寄送驗證信
## F-02 登入OAuth2 + OIDC
- [UI] 使用 `usage=web_login` OAuth client導向 `/oauth/authorize` 完成 Authorization Code + PKCE
- [UI] 若未登入,會員中心會導向 Web login登入後回到原 authorize request
- [UI] 站點用 code + code_verifier 換 token
- [UI] `web_login` 可使用 public client不要求 client secret必須設定 redirect URI
- [UI] 若 Web 與 API 不同 origin需設定 `Auth:WebLoginUrl`,且 Web 端需允許導回 `Auth:Issuer``Auth:AllowedLoginReturnUrlPrefixes`
- [UI] 若 Web 與 API 位於不同子網域,需設定 `Auth:CookieDomain`,讓 authorize endpoint 可讀取 Web login cookie
- [Auth] 不支援 password grant 或未註冊 clientrefresh token 統一送至 `/oauth/token`
- [Auth] 新簽發 token 套用後台安全設定access token `51440` 分鐘、refresh token `1365`
## F-02a 登出(外站 direct logout
- [UI] 外站將瀏覽器導向 Member Center Web `/account/logout`
- [UI] Member Center 清除 Web login cookie / session
- [UI] 若帶 `returnUrl`Member Center 僅允許導回本站 local URL 或 allowlist 內的外站 URL
- [Auth] Login 與 Logout 使用各自 allowlist並結構化比對 scheme、host、port 與 path segment禁止單純字串 prefix
- [UI] 登出完成後導回外站 callback
- [UI] 外站 callback 清除本站 session / token
## F-02b 內容站台呼叫 Send EngineClient Credentials + JWT 驗簽)
- [API] 內容站台以 `client_credentials` 呼叫 `POST /oauth/token` 取得 access_token`usage=send_api`
- [API] 內容站台帶 Bearer token 呼叫 Send Engine 建立發送任務
- [API] Send Engine 以 Member Center JWKS 驗簽 token
- [API] 驗證 `scope/tenant_id/exp` 通過後才受理任務
## F-02c 檔案上傳A service -> File Space
- [API] `A service``client_credentials` 向 Member Center 取得 access token
- [API] token 需包含 `files:upload.write`
- [API] token 必須包含有效 `tenant_id`,並以 `aud=file_access_api` 為目標資源;缺少或不符時拒絕
- [API] `A service` 帶 token 呼叫 access agent / file space 上傳檔案
- [API] access agent 以 JWKS 驗簽 JWT並驗 `iss/aud/exp/scope/tenant_id`
## F-02d 檔案下載A service -> client -> File Space
- [API] client 向 `A service` 請求下載檔案
- [API] `A service` 以自己的商業邏輯驗證該 user 是否可讀取指定檔案
- [API] `A service``files:download.delegate` 呼叫 Member Center `POST /file-access/download-tokens` 取得短效 download token
- [API] download token 由 Member Center 簽發,至少綁定 `tenant_id + user_id + file_id/object_key + method=GET + exp`
- [UI/API] `A service` 將帶短效 token 的下載 URL 回給 client
- [UI/API] client 直接向 access agent / file space 請求檔案
- [API] access agent 以 `files:download.read` 呼叫 Member Center `POST /file-access/download-tokens/validate`,確認 token 與實際 GET request 邊界一致後放行
- [API] issuing 與 validation 都採 tenant fail-closedBearer token 缺少或帶錯 `tenant_id` 時不得簽發或驗證成功
註記:下載流程不直接暴露一般 S2S token 給 client。
## F-03 忘記密碼 / 重設密碼
- [API] 站點送出 `POST /auth/password/forgot`
- [UI] 會員中心頁提交 email 並發送重設信
- [API/UI] 使用 token 進入重設密碼頁
註記:目前 Web UI 已實作 forgot/reset 流程,但尚未串接 email 發送;開發階段會直接顯示 reset token 與 reset 連結。
## F-03b 已登入修改密碼
- [UI] 使用者登入後進入 `/account/changepassword`
- [UI] 輸入目前密碼與新密碼
- [UI] 會員中心驗證目前密碼後更新密碼
- [UI] 更新成功後刷新登入狀態
## F-04 訂閱電子報(未登入)
- [API] 站點以 `tenant_api` client credentials 取得含 `newsletter:subscriptions.write` 的 token
- [API] 站點帶 Bearer token 送出 `POST /newsletter/subscribe`token tenant 必須擁有指定清單
- [API] 會員中心建立 pending 訂閱並發送驗證信
- [UI] 使用者點擊驗證信連結 `/newsletter/confirm?token=...`
- [UI] 訂閱改為 active發出 event `subscription.activated`
## F-05 取消訂閱(單一清單)
- [API] 站點以 tenant Bearer token 與 `list_id + email` 呼叫 `POST /newsletter/unsubscribe-token` 取得 token`newsletter:subscriptions.write`
- [UI] 使用者點擊退訂連結 `/newsletter/unsubscribe?token=...`
- [UI] 訂閱狀態改為 unsubscribed
- [API] 發出 event `subscription.unsubscribed`
## F-05b One-Click 退訂 TokenSend Engine 發信前)
- [API] Send Engine 以 `tenant_id + list_id + subscriber_id` 呼叫 `POST /newsletter/one-click-unsubscribe-token`
- [API] 或以 `tenant_id + list_id + subscriber_ids[]` 呼叫 `POST /newsletter/one-click-unsubscribe-tokens` 批次取得
- [API] Member Center 回傳 one-click `unsubscribe_token`
- [API] Send Engine 將 token 置入信件 `List-Unsubscribe` URL
## F-06 訂閱偏好管理(租戶站台 S2S
- [API] 站點以 tenant Bearer token 與 `list_id + email` 讀取 `/newsletter/preferences`;需 `newsletter:subscriptions.read`
- [API] 站點以 tenant Bearer token 與 `list_id + email` 更新 `/newsletter/preferences`;需 `newsletter:subscriptions.write`
- [API] Member Center 驗證 token tenant 擁有指定清單,缺少或不符時拒絕
- [UI] 會員中心提供偏好頁(可選)
## F-06b 我的電子報訂閱管理(登入後)
- [UI] 使用者登入後進入 `/profile/subscriptions`
- [API/UI] 會員中心依目前登入 user 查出其已綁定的訂閱清單
- [UI] 顯示各租戶 / 清單 / 狀態 / 訂閱時間
- [UI] 使用者可直接按下取消訂閱
- [API/UI] 系統驗證該訂閱屬於目前登入 user 後直接退訂
- [API/UI] 不需再次 email token 驗證
- [API] 發出 event `subscription.unsubscribed`
## F-10 Send Engine 事件同步Member Center → Send Engine
- [API] Member Center 以 webhook 推送 `subscription.activated/unsubscribed/preferences.updated`scope: `newsletter:events.write`
- [API] Header 使用 `X-Signature` / `X-Timestamp` / `X-Nonce` / `X-Client-Id`
- [API] `X-Client-Id` 對應 Send Engine `auth_clients.id`UUID
- [API] Member Center 從 DB 的 tenant 設定讀取對應 webhook client id
- [API] Send Engine 驗證簽章 + timestamp + nonce重放防護後入庫
- [API] Send Engine 更新名單快照
## F-10b 訂閱名單讀取Send Engine → Member Center
- [API] tenant client 以 `newsletter:list.read` 呼叫 `GET /newsletter/subscriptions?list_id=...`
- [API] Member Center 驗證 token `tenant_id` 與清單 tenant 一致claim 缺少、格式錯誤或不符時拒絕
- [API] 平台級跨 tenant 同步必須使用獨立的 `newsletter:list.read.global`
## F-11 黑名單回寫Send Engine → Member Center
- [API] Send Engine 依事件規則處理:
- [API] `hard_bounce` / `soft_bounce_threshold` / `suppression`:回寫後由 Member Center 取消該 email 的所有訂閱(跨租戶)並加入黑名單
- [API] `complaint`:回寫後由 Member Center 僅取消該筆訂閱,不加入黑名單
- [API] 呼叫 `POST /subscriptions/disable`
- [API] tenant client 用 `newsletter:events.write`
- [API] 平台 clientSES 聚合事件)用 `newsletter:events.write.global`
- [API] body 需含 `tenant_id + subscriber_id + list_id + reason + disabled_by + occurred_at`
- [API] `hard_bounce` / `soft_bounce_threshold` / `suppression` 才會寫入 `email_blacklist``complaint` 不寫黑名單
## F-12 Webhook Client Mapping 回填Send Engine → Member Center
- [API] Send Engine 建立/更新 tenant 對應的 webhook client`auth_clients.id`
- [API] 呼叫 `POST /integrations/send-engine/webhook-clients/upsert`scope: `newsletter:events.write.global`
- [API] Member Center 更新 tenant 設定DB
## F-07 會員資料查看
- [API] 站點讀取 `/user/profile`
- [API] `/user/profile``/user/addresses``/user/subscriptions` 僅能存取 access token subject 對應的會員資料
- [API] Email 不是公開識別資料,不提供 by-email 跨會員查詢端點
- [UI] 會員中心提供個人資料頁
## F-07b 會員資料維護
- [UI] 使用者登入後進入 `/profile`
- [UI] 維護電話、公司、統編等基本資料
- [API/UI] 儲存會員基本資料
## F-07c 地址簿管理
- [UI] 使用者登入後進入 `/profile/addresses`
- [UI] 新增、編輯、刪除收貨地址
- [UI] 可設定預設地址
- [API/UI] 地址簿資料綁定目前登入 user
- [API/UI] 若只剩最後一筆地址,不允許刪除
## F-07d 其他服務讀取會員資料
- [API] 其他服務以 access token 呼叫會員中心 profile API
- [API] token 需帶對應 profile scopes
- [API] 會員中心依 scope 決定可讀取基本資料、地址簿或訂閱資料
- [API] service API 為主要模式,只要 client 具對應 scope 即可存取對應資料
## F-08 管理者管理租戶/清單/Client
- [UI] 會員中心管理後台進行 CRUD
## F-09 訂閱與會員綁定
- [API] 使用者完成註冊後,會員中心將訂閱資料與 user_id 綁定
- [API] 發送事件 `subscription.linked_to_user`