template-monorepo/internal/model/auth/SDD.md

310 lines
9.9 KiB
Markdown
Raw Permalink Normal View History

2026-05-21 23:52:39 +00:00
# Auth Service
# Auth Service — SRS/SDD Document
| Version | Version Date | Editor | Memo |
|---------|--------------|--------|------|
| 1.0.0 | 2026/05/21 | Gateway Team | 初版Gateway Auth 模組 SDD |
---
## 1. Introduction
### 1.1 Purpose
Auth 模組為 TianTing Gateway 的**認證領域層**提供租戶範圍內的原子化認證原語邀請碼、註冊稽核、OAuth 暫存 Session、CloudEP JWT 簽發/刷新/登出。完整 HTTP 流程由 `internal/logic/auth/` 編排,並與 ZITADEL身份、Member會員、Notification通知協作。
### 1.2 Scope
**範圍內:**
- 租戶邀請碼Validate / Consume
- 註冊稽核 metadata條款版本、渠道、行銷 opt-in
- OAuth 社交註冊/登入暫存 SessionRedis
- CloudEP JWT access / refresh token 生命週期
- JWT 黑名單與 jti pair 管理
**範圍外(委派其他模組):**
- 使用者身份建立 → ZITADEL
- 會員 profile、OTP → Member 模組
- 郵件/簡訊 → Notification 模組
- RBAC 授權 → Permission 模組 + Casbin middleware
### 1.3 Definitions, Acronyms, and Abbreviation
| 縮寫 | 說明 |
|------|------|
| **JWT** | JSON Web TokenCloudEP 自簽 access / refresh token |
| **JTI** | JWT ID用於黑名單與 pair 映射 |
| **ROPG** | Resource Owner Password Grant密碼登入 |
| **OIDC** | OpenID Connect社交登入SSO |
| **OTP** | One-Time Password由 Member 模組管理 |
| **Tenant** | 租戶,多租戶隔離單位 |
| **UID** | 租戶內可讀會員識別碼(如 `ACME-10000003` |
### 1.4 Technologies to be used
| 項目 | 技術 |
|------|------|
| Application Language | Go 1.22+ |
| Framework | go-zero (`rest`, `logx`, `stores/redis`) |
| Cache / Session | Redis |
| Database | MongoDB |
| JWT | `github.com/golang-jwt/jwt/v4`HS256 |
| Identity Provider | ZITADEL`internal/library/zitadel` |
| API Codegen | goctl / `.api` 檔 |
### 1.5 Overview
Auth 模組採 Clean Architecture 分層:`domain/` 定義介面與實體,`repository/` 實作 Mongo / Redis 適配器,`usecase/` 提供原子 UseCase。HTTP handler 不直接存取 repository而是由 logic 層串接 ZITADEL + Member + Auth 原語。
主要流程:
1. **Email 註冊**Consume invite → ZITADEL 建 user → Member 建 unverified → 發 OTP → confirm 後 Activate + IssuePair
2. **密碼登入**ZITADEL ROPG → 查 Member → IssuePair
3. **社交註冊/登入**Redis Session 暫存 OAuth state → callback 換 token → Provision / Login
4. **Token 刷新/登出**Refresh 黑名單舊 pair 後重簽Logout 黑名單 access + refresh jti
---
## 2. System Overview
Auth 模組是 Gateway 認證子系統的核心,負責:
- 控制誰可以註冊(邀請碼)
- 記錄註冊合規稽核
- 管理短期 OAuth 流程狀態
- 簽發與驗證 CloudEP JWTmiddleware `AuthJWT`
對外透過 `/api/v1/auth/*` REST API 暴露JWT 驗證後將 `(tenant_id, uid)` 注入 request context供 Member / Permission 等下游使用。
---
## 3. System Architecture
### 3.1 System Architecture
```mermaid
flowchart TB
subgraph Client["Client / Frontend"]
Web[Web / Mobile App]
end
subgraph Gateway["Gateway"]
Handler["handler/auth"]
Logic["logic/auth<br/>(orchestration)"]
subgraph AuthModule["internal/model/auth"]
UC["usecase/"]
Repo["repository/"]
Domain["domain/"]
end
MW["middleware/AuthJWT"]
end
subgraph External["External Services"]
Zitadel[ZITADEL IdP]
Mongo[(MongoDB)]
Redis[(Redis)]
end
subgraph Sibling["Sibling Modules"]
Member[member]
Notif[notification]
end
Web --> Handler
Handler --> Logic
Logic --> UC
Logic --> Zitadel
Logic --> Member
Logic --> Notif
UC --> Repo
Repo --> Mongo
Repo --> Redis
MW --> UC
```
### 3.2 Decomposition Description
| 套件 | 職責 |
|------|------|
| `config/` | JWT TTL、Session TTL 設定 |
| `domain/entity/` | Mongo 文件模型InviteCode、RegistrationMetadata |
| `domain/enum/` | RegistrationChannelemail / google |
| `domain/repository/` | Port 介面 + Redis Session struct |
| `domain/usecase/` | UseCase 介面 + DTO |
| `domain/const.go` | BSON 欄位名、Redis key、邀請碼 hash helper |
| `domain/errors.go` | 領域 sentinel errors |
| `repository/` | Mongo + Redis 適配器、`EnsureMongoIndexes` |
| `usecase/` | 實作 + `NewModuleFromParam` factory |
| `usecase/module.go` | 組裝 Invite / RegistrationMeta / Session UseCase |
**ServiceContext 注入:**
| 欄位 | UseCase | 條件 |
|------|---------|------|
| `AuthInvite` | InviteUseCase | Mongo + Redis |
| `AuthRegistrationMeta` | RegistrationMetaUseCase | Mongo |
| `AuthRegistrationSession` | RegistrationSessionUseCase | Redis |
| `AuthLoginSession` | LoginSessionUseCase | Redis |
| `AuthToken` | TokenUseCase | JWT secret + Redis獨立於 Module |
### 3.3 Token
CloudEP JWT 採 HS256access / refresh 使用不同 secret。
**Claims 結構:**
| Claim | 說明 |
|-------|------|
| `tenant_id` | 租戶 ID |
| `uid` | 會員 UID |
| `typ` | `access``refresh` |
| `auth_gen` | 簽發世代(用於強制登出) |
| `jti` | Token 唯一 ID |
| `iat` / `exp` | 簽發/過期時間 |
**預設 TTL**
| Token | 預設 |
|-------|------|
| Access | 900 秒15 分鐘) |
| Refresh | 604800 秒7 天) |
| Registration Session | 600 秒10 分鐘) |
**Redis 映射:**
| Key | 用途 |
|-----|------|
| `auth:jwt:pair:{jti}` | access ↔ refresh jti 雙向映射 |
| `auth:jwt:bl:{jti}` | 已登出/已刷新的 jti 黑名單 |
**Refresh 流程:**
```mermaid
sequenceDiagram
participant C as Client
participant L as TokenRefreshLogic
participant T as TokenUseCase
participant R as Redis
C->>L: POST /auth/token/refresh {refresh_token}
L->>T: Refresh(refreshToken)
T->>T: Parse + verify typ=refresh
T->>R: Blacklist old refresh jti + paired access jti
T->>T: IssuePair(new access + refresh)
T->>R: SavePair(new jti mapping)
T-->>L: TokenPair
L-->>C: AuthTokenData
```
### 3.4 Invite Code
邀請碼以 SHA-256 hash 儲存,明文永不落庫。
| 操作 | 行為 |
|------|------|
| **Validate** | 依 `(tenant_id, code_hash)` 查詢,檢查過期與剩餘次數 |
| **Consume** | Redis SETNX lock30s→ 原子 `$inc used_count` |
**消費時機:**
- Email 註冊:在 `/register` 起始即 Consume
- 社交註冊Start 僅 ValidateCallback 才 Consume
### 3.5 OAuth Session
| Session 類型 | Redis Key | State 前綴 | 用途 |
|-------------|-----------|-----------|------|
| Registration | `auth:register:session:{id}` | `reg:` | 社交註冊暫存 invite / terms |
| Login | `auth:login:session:{id}` | `login:` | 社交登入暫存 tenant / redirect |
---
## 4. Data Design
### 4.1 Data Dictionary
#### invite_codesMongoDB
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, code_hash) |
| code_hash | String | SHA-256(normalized code) | Unique(tenant_id, code_hash) |
| max_uses | Int64 | 最大可用次數 | — |
| used_count | Int64 | 已使用次數 | — |
| expires_at | Int64 | 過期時間 ms0=永不過期) | — |
| new_users_only | Bool | 僅限新用戶(社交註冊) | — |
| create_at | Int64 | 建立時間 ms | — |
| update_at | Int64 | 更新時間 ms | — |
#### registration_metadataMongoDB
| Field | Type | Comment | Index |
|-------|------|---------|-------|
| _id | ObjectId | PK | PK |
| tenant_id | String | 租戶 ID | Unique(tenant_id, uid) |
| uid | String | 會員 UID | Unique(tenant_id, uid) |
| invite_code_id | String | 使用的邀請碼 ID | — |
| accept_terms_version | String | 接受的服務條款版本 | — |
| marketing_opt_in | Bool | 行銷 opt-in | — |
| registration_channel | String | email / google | — |
| client_ip | String | 註冊 IP | — |
| user_agent | String | User-Agent | — |
| occurred_at | Int64 | 事件時間 ms | — |
| create_at | Int64 | 建立時間 ms | — |
#### Redis Session / Token Keys
| Key Pattern | Type | TTL | Comment |
|-------------|------|-----|---------|
| auth:register:session:{id} | String(JSON) | 600s | 社交註冊 OAuth session |
| auth:login:session:{id} | String(JSON) | 600s | 社交登入 OAuth session |
| auth:invite:consume:{tenant}:{hash} | String | 30s | 邀請碼消費鎖 |
| auth:jwt:pair:{jti} | String | token TTL | access↔refresh 映射 |
| auth:jwt:bl:{jti} | String | 至自然過期 | JWT 黑名單 |
---
## 5. API Design
**Base Path** `/api/v1/auth`
### 5.1 Public Endpoints無 Bearer
| Method | Path | 說明 |
|--------|------|------|
| POST | `/register` | Email 註冊(回傳 challenge_id |
| POST | `/register/confirm` | OTP 確認 → 核發 JWT |
| POST | `/register/resend` | 重發註冊 OTP |
| POST | `/register/social/start` | 社交註冊 OAuth 起始 |
| GET | `/register/social/callback` | 社交註冊 OAuth callback |
| POST | `/login` | 密碼登入 |
| POST | `/login/social/start` | 社交登入 OAuth 起始 |
| GET | `/login/social/callback` | 社交登入 OAuth callback |
| POST | `/token/refresh` | 刷新 token pair |
| POST | `/token/exchange` | ZITADEL id_token → CloudEP JWT |
### 5.2 Protected Endpoints需 Bearer JWT
| Method | Path | 說明 |
|--------|------|------|
| POST | `/logout` | 登出(黑名單 jti |
完整請求/回應 schema 見 `generate/api/auth.api`;成功 envelope `code=102000`
---
## 6. Resource
| 資源 | 路徑 |
|------|------|
| API 定義 | `generate/api/auth.api` |
| Logic 編排 | `internal/logic/auth/` |
| JWT Middleware | `internal/middleware/authjwt_*.go` |
| 模組原始碼 | `internal/model/auth/` |
| 設定範例 | `etc/gateway.dev.example.yaml``Auth` / `JWT` 區塊 |
| 設計參考 | `docs/identity-member-design.md` |