Files
PROJECT-CONTORL/openspec/changes/archive/2025-12-28-add-user-auth/design.md
beabigegg 1fda7da2c2 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>
2025-12-28 23:41:37 +08:00

200 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)