Files
PROJECT-CONTORL/openspec/specs/user-auth/spec.md
beabigegg df50d5e7f8 chore: archive remaining OpenSpec proposals
Archived proposals:
- add-trigger-conditions-weekly-subscription: Trigger conditions and weekly subscription
- update-api-consistency: WebSocket auth, optimistic locking, workload defaults

All implementations were already complete in previous commits (f5f870d).
Updated tasks.md with implementation summary.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 18:47:14 +08:00

267 lines
11 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.

# User Authentication & Authorization
## Purpose
使用者認證與授權系統,透過外部認證 API 進行身份驗證,提供細部權限控制。
## Requirements
### Requirement: API-Based Authentication
系統 SHALL 限定使用外部認證 API (https://pj-auth-api.vercel.app) 進行登入認證,不支援其他認證方式。
#### Scenario: API 登入成功
- **GIVEN** 使用者擁有有效的企業帳號
- **WHEN** 使用者透過前端提交憑證
- **THEN** 系統呼叫 https://pj-auth-api.vercel.app 驗證憑證
- **AND** 驗證成功後建立 session 並回傳 JWT token
#### Scenario: API 登入失敗
- **GIVEN** 使用者提供無效的憑證
- **WHEN** 使用者嘗試登入
- **THEN** 認證 API 回傳錯誤
- **AND** 系統拒絕登入並顯示錯誤訊息
- **AND** 記錄失敗的登入嘗試
#### Scenario: 認證 API 無法連線
- **GIVEN** 認證 API 服務無法連線
- **WHEN** 使用者嘗試登入
- **THEN** 系統顯示服務暫時無法使用的訊息
- **AND** 記錄連線失敗事件
### Requirement: System Administrator
系統 SHALL 預設一個系統管理員帳號,擁有所有權限。系統管理員帳號必須存在於外部認證系統,且登入流程仍需透過外部認證 API不允許本地繞過認證。
#### Scenario: 預設管理員帳號
- **GIVEN** 系統初始化完成
- **WHEN** 系統啟動
- **THEN** 存在預設管理員帳號 `ymirliu@panjit.com.tw`
- **AND** 該帳號擁有 `super_admin` 角色
- **AND** 該帳號不可被刪除或降級
#### Scenario: 管理員登入流程
- **GIVEN** 管理員帳號 `ymirliu@panjit.com.tw` 需要登入
- **WHEN** 管理員提交憑證
- **THEN** 系統仍需呼叫 https://pj-auth-api.vercel.app 驗證
- **AND** 不存在任何本地繞過認證的機制
- **AND** 驗證成功後才授予 `super_admin` 權限
#### Scenario: 管理員全域權限
- **GIVEN** 管理員帳號 `ymirliu@panjit.com.tw` 已通過 API 認證並登入
- **WHEN** 管理員存取任何資源
- **THEN** 系統允許存取,無視部門隔離限制
### Requirement: Role-Based Access Control
系統 SHALL 支援基於角色的存取控制 (RBAC)。
#### Scenario: 角色權限檢查
- **GIVEN** 使用者被指派特定角色 (如工程師、主管、PMO)
- **WHEN** 使用者嘗試存取受保護的資源
- **THEN** 系統根據角色權限決定是否允許存取
#### Scenario: 角色指派
- **GIVEN** 管理員擁有使用者管理權限
- **WHEN** 管理員為使用者指派角色
- **THEN** 系統更新使用者的角色設定
- **AND** 新權限立即生效
### Requirement: Department Isolation
系統 SHALL 實施部門級別的資料隔離,確保跨部門資料安全。
#### Scenario: 部門資料隔離
- **GIVEN** 使用者屬於研發部門
- **WHEN** 使用者嘗試存取廠務部門的專案
- **THEN** 系統拒絕存取並顯示無權限訊息
#### Scenario: 跨部門專案存取
- **GIVEN** 專案被設定為跨部門可見
- **WHEN** 不同部門的使用者嘗試存取該專案
- **THEN** 系統根據專案的 Security_Level 設定決定是否允許存取
### Requirement: Session Management
系統 SHALL 管理使用者 session包含過期與登出機制。
#### Scenario: Session 過期
- **GIVEN** 使用者已登入系統
- **WHEN** Session 超過設定的有效期限
- **THEN** 系統自動使 session 失效
- **AND** 使用者需重新登入
#### Scenario: 主動登出
- **GIVEN** 使用者已登入系統
- **WHEN** 使用者執行登出操作
- **THEN** 系統銷毀 session 並清除 token
### Requirement: API Rate Limiting
The system SHALL implement rate limiting to protect against brute force attacks and DoS attempts.
#### Scenario: Login rate limit enforcement
- **GIVEN** a client IP has made 5 login attempts within 1 minute
- **WHEN** the client attempts another login
- **THEN** the system returns HTTP 429 Too Many Requests
- **AND** the response includes a Retry-After header
#### Scenario: Rate limit window reset
- **GIVEN** a client has exceeded the rate limit
- **WHEN** the rate limit window expires (1 minute)
- **THEN** the client can make new requests
#### Scenario: Rate limit per IP
- **GIVEN** rate limiting is IP-based
- **WHEN** different IPs make requests
- **THEN** each IP has its own rate limit counter
### Requirement: Comprehensive API Rate Limiting
The system SHALL enforce rate limits on all sensitive API endpoints to prevent abuse and ensure service availability.
#### Scenario: Task creation rate limit exceeded
- **WHEN** user exceeds 60 task creation requests per minute
- **THEN** system returns 429 Too Many Requests
- **THEN** response includes Retry-After header
#### Scenario: Report generation rate limit exceeded
- **WHEN** user exceeds 5 report generation requests per minute
- **THEN** system returns 429 Too Many Requests
- **THEN** response includes rate limit headers
#### Scenario: Rate limit headers provided
- **WHEN** user makes any rate-limited API request
- **THEN** response includes X-RateLimit-Limit header
- **THEN** response includes X-RateLimit-Remaining header
- **THEN** response includes X-RateLimit-Reset header
#### Scenario: Rate limit window reset
- **WHEN** rate limit window expires
- **THEN** user can make requests again up to the limit
- **THEN** X-RateLimit-Remaining resets to maximum
### Requirement: Input Length Validation
The system SHALL enforce maximum length limits on all user-provided string inputs to prevent DoS attacks and database overflow.
#### Scenario: Task title exceeds maximum length
- **WHEN** user submits a task with title longer than 500 characters
- **THEN** system returns 422 Validation Error with descriptive message
#### Scenario: Description field within limits
- **WHEN** user submits content with description under 10000 characters
- **THEN** system accepts the input and processes normally
### Requirement: Secure WebSocket Authentication
The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters.
#### Scenario: WebSocket connection with token in first message
- **WHEN** client connects to WebSocket endpoint without a query token
- **THEN** server waits for authentication message containing JWT token
- **THEN** server validates token before accepting further messages
- **THEN** server sends an authentication acknowledgment message
#### Scenario: WebSocket connection with invalid token
- **WHEN** client sends an invalid or expired token
- **THEN** server sends an error message indicating invalid or expired token
- **THEN** server closes the connection with an authentication error code
#### Scenario: WebSocket connection timeout without authentication
- **WHEN** client connects but does not send authentication within 10 seconds
- **THEN** server closes the connection with appropriate error code
### Requirement: Path Traversal Protection
The system SHALL prevent file path traversal attacks by validating all file paths resolve within the designated storage directory.
#### Scenario: Path traversal attempt detected
- **WHEN** request contains file path with "../" or absolute path outside storage
- **THEN** system rejects request and logs security warning
- **THEN** system returns 403 Forbidden error
#### Scenario: Valid file path within storage
- **WHEN** request contains valid relative file path
- **THEN** system resolves path and verifies it is within storage directory
- **THEN** system processes file operation normally
### Requirement: JWT Secret Validation
The system SHALL validate JWT secret key strength on startup.
#### Scenario: Weak secret rejected
- **WHEN** the configured JWT secret is less than 32 characters
- **THEN** the system SHALL log a critical warning
- **AND** optionally refuse to start in production mode
#### Scenario: Low entropy secret warning
- **WHEN** the JWT secret has low entropy (repeating patterns, common words)
- **THEN** the system SHALL log a security warning
### Requirement: CSRF Protection
The system SHALL protect sensitive state-changing operations with CSRF tokens.
#### Scenario: CSRF token required for password change
- **WHEN** a user attempts to change their password
- **AND** the request does not include a valid CSRF token
- **THEN** the request SHALL be rejected with 403 Forbidden
#### Scenario: CSRF token required for account deletion
- **WHEN** a user attempts to delete their account or resources
- **AND** the request does not include a valid CSRF token
- **THEN** the request SHALL be rejected with 403 Forbidden
#### Scenario: Valid CSRF token accepted
- **WHEN** a state-changing request includes a valid CSRF token
- **THEN** the request SHALL proceed normally
## Data Model
```
pjctrl_users
├── id: UUID (PK)
├── email: VARCHAR(200) UNIQUE
├── name: VARCHAR(200)
├── department_id: UUID (FK)
├── role_id: UUID (FK)
├── skills: JSON
├── capacity: DECIMAL (週工時上限)
├── is_active: BOOLEAN
├── is_system_admin: BOOLEAN DEFAULT false (不可修改的系統管理員標記)
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
pjctrl_departments
├── id: UUID (PK)
├── name: VARCHAR(100)
├── parent_id: UUID (FK, self-reference)
└── created_at: TIMESTAMP
pjctrl_roles
├── id: UUID (PK)
├── name: VARCHAR(50)
├── permissions: JSON
├── is_system_role: BOOLEAN DEFAULT false
└── created_at: TIMESTAMP
```
## Default Data (Seed)
```sql
-- 預設系統管理員角色
INSERT INTO pjctrl_roles (id, name, permissions, is_system_role) VALUES
('00000000-0000-0000-0000-000000000001', 'super_admin', '{"all": true}', true);
-- 預設系統管理員帳號
INSERT INTO pjctrl_users (id, email, name, role_id, is_active, is_system_admin) VALUES
('00000000-0000-0000-0000-000000000001', 'ymirliu@panjit.com.tw', 'System Administrator',
'00000000-0000-0000-0000-000000000001', true, true);
```
## External Dependencies
- **Authentication API**: https://pj-auth-api.vercel.app (唯一認證方式)
## Authentication Flow
```
┌─────────┐ ┌─────────────┐ ┌──────────────────────────┐
│ User │────▶│ Frontend │────▶│ pj-auth-api.vercel.app │
└─────────┘ └─────────────┘ └──────────────────────────┘
│ │
│◀────── JWT Token ───────│
┌─────────────┐
│ Backend │ (驗證 JWT, 建立 Session)
└─────────────┘
```