310 lines
9.9 KiB
Markdown
310 lines
9.9 KiB
Markdown
# 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 社交註冊/登入暫存 Session(Redis)
|
||
- 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 Token,CloudEP 自簽 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 JWT(middleware `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/` | RegistrationChannel(email / 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 採 HS256,access / 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 lock(30s)→ 原子 `$inc used_count` |
|
||
|
||
**消費時機:**
|
||
|
||
- Email 註冊:在 `/register` 起始即 Consume
|
||
- 社交註冊:Start 僅 Validate;Callback 才 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_codes(MongoDB)
|
||
|
||
| 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 | 過期時間 ms(0=永不過期) | — |
|
||
| new_users_only | Bool | 僅限新用戶(社交註冊) | — |
|
||
| create_at | Int64 | 建立時間 ms | — |
|
||
| update_at | Int64 | 更新時間 ms | — |
|
||
|
||
#### registration_metadata(MongoDB)
|
||
|
||
| 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` |
|