# 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
(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` |