# Design: add-user-auth ## Context 這是專案的第一個模組,需要建立認證與授權的基礎架構。系統使用外部認證 API 進行身份驗證,本地不儲存密碼。認證成功後由本地系統核發 JWT Token 並管理 Session。 ### Stakeholders - 所有系統使用者(工程師、主管、PMO) - 系統管理員 (ymirliu@panjit.com.tw) - 外部認證 API 維護者 ### Constraints - 必須使用外部 API 認證,不可本地繞過 - 資料表必須使用 `pjctrl_` 前綴 - 需支援部門級資料隔離 ## Goals / Non-Goals ### Goals - 實現安全的外部 API 認證整合 - 建立 RBAC 權限控制框架 - 支援部門級資料隔離 - 預設系統管理員帳號 ### Non-Goals - 本地密碼儲存與驗證 - 多因素認證 (MFA) - 由外部 API 處理 - OAuth2 第三方登入 - 使用者自助註冊 ## Decisions ### Decision 1: 認證流程架構 **選擇**: Frontend-to-External-API 模式 ``` ┌─────────┐ ┌─────────────┐ ┌──────────────────────────┐ │ User │────▶│ Frontend │────▶│ pj-auth-api.vercel.app │ └─────────┘ └─────────────┘ └──────────────────────────┘ │ │ │◀────── Auth Token ──────│ │ ▼ ┌─────────────┐ │ Backend │ │ - 驗證 Token│ │ - 核發 JWT │ │ - 建立 Session └─────────────┘ ``` **原因**: - 減少後端介入認證流程的攻擊面 - 外部 API 已處理密碼安全性 - 後端只負責驗證與授權 **替代方案考量**: - Backend Proxy 模式:增加延遲與複雜度,無明顯優勢 ### Decision 2: JWT Token 策略 **選擇**: 短期 Access Token + Redis Session ```python JWT_PAYLOAD = { "sub": "user_id", "email": "user@example.com", "role": "engineer", "department_id": "uuid", "is_system_admin": false, "exp": "15 minutes from now", "iat": "now" } ``` **原因**: - 短期 Token 減少被盜用風險 - Redis Session 可即時撤銷 - 權限資訊嵌入 Token 減少 DB 查詢 ### Decision 3: 權限模型 **選擇**: RBAC + 部門隔離混合模式 ```python # 權限檢查順序 def check_permission(user, resource, action): # 1. 系統管理員 - 全權通過 if user.is_system_admin: return True # 2. 角色權限檢查 if not has_role_permission(user.role, action): return False # 3. 部門隔離檢查 if not is_same_department_or_allowed(user, resource): return False return True ``` **原因**: - 系統管理員需無限制存取(用於問題排查) - RBAC 提供功能層面控制 - 部門隔離提供資料層面控制 ### Decision 4: 資料表設計 ```sql -- 使用 UUID 而非 auto-increment -- 便於分散式環境與資料合併 CREATE TABLE pjctrl_roles ( id CHAR(36) PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE, permissions JSON NOT NULL, is_system_role BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE pjctrl_departments ( id CHAR(36) PRIMARY KEY, name VARCHAR(100) NOT NULL, parent_id CHAR(36) REFERENCES pjctrl_departments(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE pjctrl_users ( id CHAR(36) PRIMARY KEY, email VARCHAR(200) NOT NULL UNIQUE, name VARCHAR(200) NOT NULL, department_id CHAR(36) REFERENCES pjctrl_departments(id), role_id CHAR(36) REFERENCES pjctrl_roles(id), skills JSON, capacity DECIMAL(5,2) DEFAULT 40.00, is_active BOOLEAN DEFAULT TRUE, is_system_admin BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` ## Risks / Trade-offs | 風險 | 影響 | 緩解措施 | |------|------|---------| | 外部 API 不可用 | 所有使用者無法登入 | 顯示友善錯誤訊息、記錄事件、監控告警 | | JWT Token 洩漏 | 短期內帳號被盜用 | 15 分鐘過期、Redis 可即時撤銷 | | 部門隔離邏輯錯誤 | 資料外洩 | 完整測試案例、程式碼審查 | ## Migration Plan 1. 執行資料庫 migration 建立資料表 2. 執行 seed data 建立預設角色與管理員 3. 部署後端認證模組 4. 部署前端登入頁面 5. 驗證管理員可登入 **Rollback**: - 資料庫:執行 down migration - 程式碼:回復至前一版本 ## Environment Configuration ```bash # Database MYSQL_HOST=mysql.theaken.com MYSQL_PORT=33306 MYSQL_USER=A060 MYSQL_PASSWORD=<見密碼管理> MYSQL_DATABASE=db_A060 # Redis (待設定) REDIS_HOST=localhost REDIS_PORT=6379 # JWT JWT_SECRET_KEY=<生成隨機金鑰> JWT_ALGORITHM=HS256 JWT_EXPIRE_MINUTES=15 # External Auth API AUTH_API_URL=https://pj-auth-api.vercel.app ``` ## Admin Credentials - **Email**: `ymirliu@panjit.com.tw` - **Password**: 由外部認證 API 管理(已設定於外部系統) ## Open Questions 1. ~~外部認證 API 的回應格式?~~ (需確認) 2. ~~Session 過期時間?~~ 建議 15 分鐘,可透過環境變數調整 3. ~~是否需要 Refresh Token 機制?~~ 暫不需要,視實際使用情況再評估 4. ~~MySQL 連線資訊?~~ 已提供 5. ~~Admin 帳號密碼?~~ 已提供 (ymirliu@panjit.com.tw)