# Member Service # Member Service — SRS/SDD Document | Version | Version Date | Editor | Memo | |---------|--------------|--------|------| | 1.0.0 | 2026/05/21 | Gateway Team | 初版:Gateway Member 模組 SDD | --- ## 1. Introduction ### 1.1 Purpose Member 模組為 TianTing Gateway 的**會員核心領域層**,管理多租戶下的 Tenant(租戶)、Member(會員 profile)、Identity(外部身份對映),以及 UID 生成、業務 Email/Phone OTP 驗證、TOTP step-up MFA、重發/每日配額等原子業務原語。 ### 1.2 Scope **範圍內:** - 租戶建立與 slug 解析 - 會員生命週期(unverified → active → suspended → deleted) - 外部身份 Provision(OIDC / LDAP / SCIM) - Profile 讀寫與業務 contact 驗證標記 - OTP challenge(bcrypt + Redis) - TOTP 綁定/驗證/備援碼(AES-GCM 保護 secret) - 可讀 UID 序號生成 **範圍外:** - 密碼驗證、JWT 簽發 → Auth 模組 + ZITADEL - 通知寄送 → Notification 模組(logic 層編排) - RBAC → Permission 模組 **架構原則:** usecase **不可**呼叫其他 usecase;多步流程由 logic 層編排。 ### 1.3 Definitions, Acronyms, and Abbreviation | 縮寫 | 說明 | |------|------| | **Tenant** | 租戶,B2B 客戶組織單位 | | **Member** | 租戶範圍內的會員 profile | | **Identity** | 外部 ID(LDAP/SCIM external_id 或 zitadel_sub)→ UID 對映 | | **UID** | 可讀主鍵,格式 `{UIDPrefix}-{Sequence}`(如 `ACME-10000003`) | | **OTP** | 一次性數字驗證碼(Email/Phone 業務驗證) | | **TOTP** | RFC 6238 時間型 OTP(Google Authenticator) | | **Origin** | 會員來源:platform_native / oidc / ldap / scim | ### 1.4 Technologies to be used | 項目 | 技術 | |------|------| | Application Language | Go 1.22+ | | Framework | go-zero | | Cache | Redis(OTP、TOTP enroll、UID seq、verify rate) | | Database | MongoDB | | Crypto | bcrypt(OTP)、AES-GCM(TOTP secret)、RFC 6238 TOTP | | API Codegen | goctl / `.api` 檔 | ### 1.5 Overview Member 模組提供 7 個 atomic UseCase,由 `NewModuleFromParam` 依 Mongo / Redis / TOTP KEK 條件組裝: | UseCase | 條件 | |---------|------| | OTP、VerifyRate | Redis 必填 | | Profile、Lifecycle、Tenant、Provisioning | Mongo 設定後 | | TOTP | Mongo + `TOTP.SecretKEK` 設定後 | --- ## 2. System Overview Member 模組是多租戶身份系統的資料核心: - 每個 Member 必屬一個 Tenant - UID 以 `{TenantUIDPrefix}-{Sequence}` 生成,序號從 10,000,000 起跳 - 平台註冊走 `unverified → active`;OIDC/LDAP/SCIM 首登直接 `active` - 業務 Email/Phone 驗證與 TOTP 為 step-up MFA 能力 對外透過 `/api/v1/members/*` REST API 暴露(profile、verification、TOTP)。 --- ## 3. System Architecture ### 3.1 System Architecture ```mermaid flowchart TB subgraph Client["Client"] App[Frontend / API Client] end subgraph Gateway["Gateway"] Handler["handler/member"] Logic["logic/member
(orchestration)"] subgraph MemberModule["internal/model/member"] UC["usecase/ (7 atomic)"] Repo["repository/"] TOTP["totp/ (RFC 6238)"] end end subgraph Storage["Storage"] Mongo[(MongoDB)] Redis[(Redis)] end subgraph Sibling["Sibling Modules"] Auth[auth] Notif[notification] end App --> Handler Handler --> Logic Logic --> UC Logic --> Notif Auth --> UC UC --> Repo UC --> TOTP Repo --> Mongo Repo --> Redis ``` ### 3.2 Decomposition Description | 套件 | 職責 | |------|------| | `config/` | OTP / TOTP / Registration 設定 | | `domain/entity/` | Member、Tenant、Identity Mongo doc | | `domain/enum/` | MemberStatus、MemberOrigin、OTPPurpose、TenantStatus | | `domain/repository/` | 7 個 repository 介面 | | `domain/usecase/` | 7 個 usecase 介面 + DTO | | `domain/redis.go` | Redis key helper(禁止字串拼接) | | `repository/` | Mongo + Redis 實作 | | `totp/` | RFC 6238 純函式(secret、verify、otpauth URL) | | `usecase/` | 實作 + module factory + mapper | ### 3.3 Member Lifecycle ```mermaid stateDiagram-v2 [*] --> unverified: Lifecycle.CreateUnverified
(platform 註冊) [*] --> active: Provisioning.Ensure*
(OIDC/LDAP/SCIM) unverified --> active: Activate (OTP 通過) unverified --> deleted: AbortPending (註冊逾時) active --> suspended: Suspend suspended --> active: Reactivate active --> deleted: SoftDelete suspended --> deleted: SoftDelete deleted --> [*] ``` ### 3.4 UID Generation ```mermaid sequenceDiagram participant UC as Lifecycle / Provisioning participant Gen as UIDGenerator participant R as Redis UC->>Gen: Next(tenant, uidPrefix) Gen->>R: INCR member:seq:{tenant} alt seq == 1 (首次) Gen->>R: INCRBY (UIDSequenceStart - 1) end Gen-->>UC: "{PREFIX}-{seq}" ``` - `UIDSequenceStart = 10_000_000` - `UIDPrefix` 限制 2~4 個大寫字母 ### 3.5 OTP / TOTP **OTP:** bcrypt hash 存 Redis challenge;Verify 成功後立即刪除(一次性)。 **TOTP:** secret 以 AES-GCM cipher 存 Mongo;enroll 階段 staged secret 存 Redis(TTL 600s);VerifyCode 含 replay 保護(timestep 去重)。 --- ## 4. Data Design ### 4.1 Data Dictionary #### tenants(MongoDB) | Field | Type | Comment | Index | |-------|------|---------|-------| | _id | ObjectId | PK | PK | | tenant_id | String | 租戶 ID | — | | slug | String | URL slug | Unique | | name | String | 顯示名稱 | — | | uid_prefix | String | UID 前綴(2-4 大寫) | Unique | | status | String | active / suspended | — | | org_id | String | 外部 org ID | — | | create_at | Int64 | 建立時間 ms | — | | update_at | Int64 | 更新時間 ms | — | #### members(MongoDB) | Field | Type | Comment | Index | |-------|------|---------|-------| | _id | ObjectId | PK | PK | | tenant_id | String | 租戶 ID | Unique(tenant_id, uid) | | uid | String | 可讀 UID | Unique(tenant_id, uid) | | zitadel_user_id | String | ZITADEL sub | Unique(tenant_id, zitadel_user_id) sparse | | zitadel_email | String | IdP email | — | | display_name | String | 顯示名稱 | — | | avatar | String | 頭像 URL | — | | phone | String | 聯絡電話 | — | | language | String | 語系 | — | | currency | String | 幣別 | — | | member_status | String | unverified/active/suspended/deleted | — | | origin | String | platform_native/oidc/ldap/scim | — | | business_email | String | 業務 email | — | | business_email_verified | Bool | 業務 email 已驗證 | — | | business_phone | String | 業務 phone | — | | business_phone_verified | Bool | 業務 phone 已驗證 | — | | totp_enrolled | Bool | TOTP 已綁定 | — | | totp_secret_cipher | Binary | AES-GCM 加密 secret | — | | totp_backup_codes_hash | Array | bcrypt 備援碼 hash | — | | suspend_reason | String | 停權原因 | — | | create_at | Int64 | 建立時間 ms | — | | update_at | Int64 | 更新時間 ms | — | | deleted_at | Int64 | 軟刪時間 ms | — | #### identities(MongoDB) | Field | Type | Comment | Index | |-------|------|---------|-------| | _id | ObjectId | PK | PK | | tenant_id | String | 租戶 ID | Unique(tenant_id, external_id) | | external_id | String | LDAP/SCIM 外部 ID | Unique(tenant_id, external_id) | | zitadel_user_id | String | ZITADEL sub | Unique(tenant_id, zitadel_user_id) | | uid | String | 對應 Member UID | — | | create_at | Int64 | 建立時間 ms | — | #### Redis Keys | Key Pattern | 用途 | TTL | |-------------|------|-----| | member:otp:challenge:{id} | OTP challenge(bcrypt hash) | 300s | | member:otp:challenge:{id}:attempts | OTP 錯誤次數 | 同 challenge | | member:verify:rate:{tenant}:{uid}:{kind} | 重發冷卻 | 60s | | member:verify:daily:{tenant}:{uid}:{kind} | 每日上限計數 | 24h | | member:totp:enroll:{tenant}:{uid} | TOTP 綁定 staged secret | 600s | | member:totp:used:{tenant}:{uid}:{step} | TOTP 重放保護 | 90s | | member:seq:{tenant} | UID 序號 INCR | 永久 | --- ## 5. API Design **Base Path:** `/api/v1/members` ### 5.1 Profile | Method | Path | 說明 | |--------|------|------| | GET | `/me` | 取得當前會員 profile | | PATCH | `/me` | 更新 display_name / avatar / language 等 | ### 5.2 Verification(OTP) | Method | Path | 說明 | |--------|------|------| | POST | `/me/verifications/email/start` | 發起業務 email OTP | | POST | `/me/verifications/email/confirm` | 確認 email OTP | | POST | `/me/verifications/phone/start` | 發起業務 phone OTP | | POST | `/me/verifications/phone/confirm` | 確認 phone OTP | ### 5.3 TOTP | Method | Path | 說明 | |--------|------|------| | GET | `/me/totp/status` | TOTP 綁定狀態 | | POST | `/me/totp/enroll` | 開始綁定(回 otpauth_url) | | POST | `/me/totp/enroll/confirm` | 確認綁定(回 backup codes) | | POST | `/me/totp/verify` | Step-up 驗碼 | | POST | `/me/totp/disable` | 解除綁定 | | POST | `/me/totp/backup-codes/regenerate` | 重產備援碼 | 完整 schema 見 `generate/api/member.api`。 --- ## 6. Resource | 資源 | 路徑 | |------|------| | API 定義 | `generate/api/member.api` | | Logic 編排 | `internal/logic/member/` | | 模組原始碼 | `internal/model/member/` | | 開發 README | `internal/model/member/README.md` | | 設定範例 | `etc/gateway.dev.example.yaml` → `Member` 區塊 | | 設計參考 | `docs/identity-member-design.md`、`docs/model.md` §6.1 | | Seed CLI | `cmd/member-seed` |