301 lines
9.4 KiB
Markdown
301 lines
9.4 KiB
Markdown
|
|
# 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<br/>(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<br/>(platform 註冊)
|
|||
|
|
[*] --> active: Provisioning.Ensure*<br/>(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` |
|