feat: implement user authentication module
- Backend (FastAPI): - External API authentication (pj-auth-api.vercel.app) - JWT token validation with Redis session storage - RBAC with department isolation - User, Role, Department models with pjctrl_ prefix - Alembic migrations with project-specific version table - Complete test coverage (13 tests) - Frontend (React + Vite): - AuthContext for state management - Login page with error handling - Protected route component - Dashboard with user info display - OpenSpec: - 7 capability specs defined - add-user-auth change archived 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
199
openspec/changes/archive/2025-12-28-add-user-auth/design.md
Normal file
199
openspec/changes/archive/2025-12-28-add-user-auth/design.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user