# External API Authentication Implementation - Complete ✅ ## 實作日期 2025-11-14 ## 狀態 ✅ **後端實作完成** - Phase 1-8 已完成 ⏳ **前端實作待續** - Phase 9-11 待實作 📋 **測試與文檔** - Phase 12-13 待完成 --- ## 📋 已完成階段 (Phase 1-8) ### Phase 1: 資料庫架構設計 ✅ #### 創建的模型文件: 1. **`backend/app/models/user_v2.py`** - 新用戶模型 - 資料表:`tool_ocr_users` - 欄位:`id`, `email`, `display_name`, `created_at`, `last_login`, `is_active` - 特點:無密碼欄位(外部認證)、email 作為主要識別 2. **`backend/app/models/task.py`** - 任務模型 - 資料表:`tool_ocr_tasks`, `tool_ocr_task_files` - 任務狀態:PENDING, PROCESSING, COMPLETED, FAILED - 用戶隔離:外鍵關聯 `user_id`,CASCADE 刪除 3. **`backend/app/models/session.py`** - Session 管理 - 資料表:`tool_ocr_sessions` - 儲存:access_token, id_token, refresh_token (加密) - 追蹤:expires_at, ip_address, user_agent, last_accessed_at #### 資料庫遷移: - **檔案**:`backend/alembic/versions/5e75a59fb763_add_external_auth_schema_with_task_.py` - **狀態**:已套用 (alembic stamp head) - **變更**:創建 4 個新表 (users, sessions, tasks, task_files) - **策略**:保留舊表,不刪除(避免外鍵約束錯誤) --- ### Phase 2: 配置管理 ✅ #### 環境變數 (`.env.local`): ```bash # External Authentication EXTERNAL_AUTH_API_URL=https://pj-auth-api.vercel.app EXTERNAL_AUTH_ENDPOINT=/api/auth/login EXTERNAL_AUTH_TIMEOUT=30 TOKEN_REFRESH_BUFFER=300 # Task Management DATABASE_TABLE_PREFIX=tool_ocr_ ENABLE_TASK_HISTORY=true TASK_RETENTION_DAYS=30 MAX_TASKS_PER_USER=1000 ``` #### 配置類 (`backend/app/core/config.py`): - 新增外部認證配置屬性 - 新增 `external_auth_full_url` property - 新增任務管理配置參數 --- ### Phase 3: 服務層實作 ✅ #### 1. 外部認證服務 (`backend/app/services/external_auth_service.py`) **核心功能:** ```python class ExternalAuthService: async def authenticate_user(username, password) -> tuple[bool, AuthResponse, error] # 呼叫外部 API:POST https://pj-auth-api.vercel.app/api/auth/login # 重試邏輯:3 次,指數退避 # 返回:success, auth_data (tokens + user_info), error_msg async def validate_token(access_token) -> tuple[bool, payload] # TODO: 完整 JWT 驗證(簽名、過期時間等) def is_token_expiring_soon(expires_at) -> bool # 檢查是否在 TOKEN_REFRESH_BUFFER 內過期 ``` **錯誤處理:** - HTTP 超時自動重試 - 5xx 錯誤指數退避 - 完整日誌記錄 #### 2. 任務管理服務 (`backend/app/services/task_service.py`) **核心功能:** ```python class TaskService: # 創建與查詢 def create_task(db, user_id, filename, file_type) -> Task def get_task_by_id(db, task_id, user_id) -> Task # 用戶隔離 def get_user_tasks(db, user_id, status, skip, limit) -> (tasks, total) # 更新 def update_task_status(db, task_id, user_id, status, error, time_ms) -> Task def update_task_results(db, task_id, user_id, paths...) -> Task # 刪除與清理 def delete_task(db, task_id, user_id) -> bool def auto_cleanup_expired_tasks(db) -> int # 根據 TASK_RETENTION_DAYS # 統計 def get_user_stats(db, user_id) -> dict # 按狀態統計 ``` **安全特性:** - 所有查詢強制 `user_id` 過濾 - 自動任務限額檢查 - 過期任務自動清理 --- ### Phase 4-6: API 端點實作 ✅ #### 1. 認證端點 (`backend/app/routers/auth_v2.py`) **路由前綴**:`/api/v2/auth` | 端點 | 方法 | 描述 | 認證 | |------|------|------|------| | `/login` | POST | 外部 API 登入 | 無 | | `/logout` | POST | 登出 (刪除 session) | 需要 | | `/me` | GET | 獲取當前用戶資訊 | 需要 | | `/sessions` | GET | 列出用戶所有 sessions | 需要 | **Login 流程:** ``` 1. 呼叫外部 API 認證 2. 獲取 access_token, id_token, user_info 3. 在資料庫中創建/更新用戶 (email) 4. 創建 session 記錄 (tokens, IP, user agent) 5. 生成內部 JWT (包含 user_id, session_id) 6. 返回內部 JWT 給前端 ``` #### 2. 任務管理端點 (`backend/app/routers/tasks.py`) **路由前綴**:`/api/v2/tasks` | 端點 | 方法 | 描述 | 認證 | |------|------|------|------| | `/` | POST | 創建新任務 | 需要 | | `/` | GET | 列出用戶任務 (分頁/過濾) | 需要 | | `/stats` | GET | 獲取任務統計 | 需要 | | `/{task_id}` | GET | 獲取任務詳情 | 需要 | | `/{task_id}` | PATCH | 更新任務 | 需要 | | `/{task_id}` | DELETE | 刪除任務 | 需要 | **查詢參數:** - `status`: pending/processing/completed/failed - `page`: 頁碼 (從 1 開始) - `page_size`: 每頁筆數 (max 100) - `order_by`: 排序欄位 (created_at/updated_at/completed_at) - `order_desc`: 降序排列 #### 3. Schema 定義 **認證** (`backend/app/schemas/auth.py`): - `LoginRequest`: username, password - `Token`: access_token, token_type, expires_in, user (V2) - `UserInfo`: id, email, display_name - `UserResponse`: 完整用戶資訊 - `TokenData`: JWT payload 結構 **任務** (`backend/app/schemas/task.py`): - `TaskCreate`: filename, file_type - `TaskUpdate`: status, error_message, paths... - `TaskResponse`: 任務基本資訊 - `TaskDetailResponse`: 任務 + 文件列表 - `TaskListResponse`: 分頁結果 - `TaskStatsResponse`: 統計數據 --- ### Phase 7: JWT 驗證依賴 ✅ #### 更新 `backend/app/core/deps.py` **新增 V2 依賴:** ```python def get_current_user_v2(credentials, db) -> UserV2: # 1. 解析 JWT token # 2. 從資料庫查詢用戶 (tool_ocr_users) # 3. 檢查用戶是否活躍 # 4. 驗證 session (如果有 session_id) # 5. 檢查 session 是否過期 # 6. 更新 last_accessed_at # 7. 返回用戶對象 def get_current_active_user_v2(current_user) -> UserV2: # 確保用戶處於活躍狀態 ``` **安全檢查:** - JWT 簽名驗證 - 用戶存在性檢查 - 用戶活躍狀態檢查 - Session 有效性檢查 - Session 過期時間檢查 --- ### Phase 8: 路由註冊 ✅ #### 更新 `backend/app/main.py` ```python # Legacy V1 routers (保留向後兼容) from app.routers import auth, ocr, export, translation # V2 routers (新外部認證系統) from app.routers import auth_v2, tasks app.include_router(auth.router) # V1: /api/v1/auth app.include_router(ocr.router) # V1: /api/v1/ocr app.include_router(export.router) # V1: /api/v1/export app.include_router(translation.router) # V1: /api/v1/translation app.include_router(auth_v2.router) # V2: /api/v2/auth app.include_router(tasks.router) # V2: /api/v2/tasks ``` **版本策略:** - V1 API 保持不變 (向後兼容) - V2 API 使用新認證系統 - 前端可逐步遷移 --- ## 🔐 安全特性 ### 1. 用戶隔離 - ✅ 所有任務查詢強制 `user_id` 過濾 - ✅ 用戶 A 無法訪問用戶 B 的任務 - ✅ Row-level security 在服務層實施 - ✅ 外鍵 CASCADE 刪除保證資料一致性 ### 2. Session 管理 - ✅ 追蹤 IP 位址和 User Agent - ✅ 自動過期檢查 - ✅ 最後訪問時間更新 - ⚠️ Token 加密待實作 (目前明文儲存) ### 3. 認證流程 - ✅ 外部 API 認證 (Azure AD) - ✅ 內部 JWT 生成 (包含 user_id + session_id) - ✅ 雙重驗證 (JWT + session 檢查) - ✅ 錯誤重試機制 (3 次,指數退避) ### 4. 資料庫安全 - ✅ 資料表前綴命名空間隔離 (`tool_ocr_`) - ✅ 索引優化 (email, task_id, status, created_at) - ✅ 外鍵約束確保參照完整性 - ✅ 軟刪除支援 (file_deleted flag) --- ## 📊 資料庫架構 ### 資料表關係圖: ``` tool_ocr_users (1) ├── tool_ocr_sessions (N) [FK: user_id, CASCADE] └── tool_ocr_tasks (N) [FK: user_id, CASCADE] └── tool_ocr_task_files (N) [FK: task_id, CASCADE] ``` ### 索引策略: ```sql -- 用戶表 CREATE INDEX ix_tool_ocr_users_email ON tool_ocr_users(email); -- 登入查詢 CREATE INDEX ix_tool_ocr_users_is_active ON tool_ocr_users(is_active); -- Session 表 CREATE INDEX ix_tool_ocr_sessions_user_id ON tool_ocr_sessions(user_id); CREATE INDEX ix_tool_ocr_sessions_expires_at ON tool_ocr_sessions(expires_at); -- 過期檢查 CREATE INDEX ix_tool_ocr_sessions_created_at ON tool_ocr_sessions(created_at); -- 任務表 CREATE UNIQUE INDEX ix_tool_ocr_tasks_task_id ON tool_ocr_tasks(task_id); -- UUID 查詢 CREATE INDEX ix_tool_ocr_tasks_user_id ON tool_ocr_tasks(user_id); -- 用戶查詢 CREATE INDEX ix_tool_ocr_tasks_status ON tool_ocr_tasks(status); -- 狀態過濾 CREATE INDEX ix_tool_ocr_tasks_created_at ON tool_ocr_tasks(created_at); -- 排序 CREATE INDEX ix_tool_ocr_tasks_filename ON tool_ocr_tasks(filename); -- 搜尋 -- 任務文件表 CREATE INDEX ix_tool_ocr_task_files_task_id ON tool_ocr_task_files(task_id); CREATE INDEX ix_tool_ocr_task_files_file_hash ON tool_ocr_task_files(file_hash); -- 去重 ``` --- ## 🧪 測試端點 (Swagger UI) ### 訪問 API 文檔: ``` http://localhost:8000/docs ``` ### 測試流程: #### 1. 登入測試 ```bash POST /api/v2/auth/login Content-Type: application/json { "username": "user@example.com", "password": "your_password" } # 成功回應: { "access_token": "eyJhbGc...", "token_type": "bearer", "expires_in": 86400, "user": { "id": 1, "email": "user@example.com", "display_name": "User Name" } } ``` #### 2. 獲取當前用戶 ```bash GET /api/v2/auth/me Authorization: Bearer eyJhbGc... # 回應: { "id": 1, "email": "user@example.com", "display_name": "User Name", "created_at": "2025-11-14T16:00:00", "last_login": "2025-11-14T16:30:00", "is_active": true } ``` #### 3. 創建任務 ```bash POST /api/v2/tasks/ Authorization: Bearer eyJhbGc... Content-Type: application/json { "filename": "document.pdf", "file_type": "application/pdf" } # 回應: { "id": 1, "user_id": 1, "task_id": "550e8400-e29b-41d4-a716-446655440000", "filename": "document.pdf", "file_type": "application/pdf", "status": "pending", "created_at": "2025-11-14T16:35:00", ... } ``` #### 4. 列出任務 ```bash GET /api/v2/tasks/?status=completed&page=1&page_size=10 Authorization: Bearer eyJhbGc... # 回應: { "tasks": [...], "total": 25, "page": 1, "page_size": 10, "has_more": true } ``` #### 5. 獲取統計 ```bash GET /api/v2/tasks/stats Authorization: Bearer eyJhbGc... # 回應: { "total": 25, "pending": 3, "processing": 2, "completed": 18, "failed": 2 } ``` --- ## ⚠️ 待實作項目 ### 高優先級 (阻塞性): 1. **Token 加密** - Session 表中的 tokens 目前明文儲存 - 需要:AES-256 加密 - 位置:`backend/app/routers/auth_v2.py` login endpoint 2. **完整 JWT 驗證** - 目前僅解碼,未驗證簽名 - 需要:Azure AD 公鑰驗證 - 位置:`backend/app/services/external_auth_service.py` 3. **前端實作** - Phase 9-11 - 認證服務 (token 管理) - 任務歷史 UI 頁面 - API 整合 ### 中優先級 (功能性): 4. **Token 刷新機制** - 自動刷新即將過期的 token 5. **檔案上傳整合** - 將 OCR 服務與新任務系統整合 6. **任務通知** - 任務完成時通知用戶 7. **錯誤追蹤** - 詳細的錯誤日誌和監控 ### 低優先級 (優化): 8. **效能測試** - 大量任務的查詢效能 9. **快取層** - Redis 快取用戶 session 10. **API 速率限制** - 防止濫用 11. **文檔生成** - 自動生成 API 文檔 --- ## 📝 遷移指南 (前端開發者) ### 1. 更新登入流程 **舊 V1 方式:** ```typescript // V1: Local authentication const response = await fetch('/api/v1/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) }); const { access_token } = await response.json(); ``` **新 V2 方式:** ```typescript // V2: External Azure AD authentication const response = await fetch('/api/v2/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) // Same interface! }); const { access_token, user } = await response.json(); // Store token and user info localStorage.setItem('token', access_token); localStorage.setItem('user', JSON.stringify(user)); ``` ### 2. 使用新的任務 API ```typescript // 獲取任務列表 const response = await fetch('/api/v2/tasks/?page=1&page_size=20', { headers: { 'Authorization': `Bearer ${token}` } }); const { tasks, total, has_more } = await response.json(); // 獲取統計 const statsResponse = await fetch('/api/v2/tasks/stats', { headers: { 'Authorization': `Bearer ${token}` } }); const stats = await statsResponse.json(); // { total: 25, pending: 3, processing: 2, completed: 18, failed: 2 } ``` ### 3. 處理認證錯誤 ```typescript const response = await fetch('/api/v2/tasks/', { headers: { 'Authorization': `Bearer ${token}` } }); if (response.status === 401) { // Token 過期或無效,重新登入 if (data.detail === "Session expired, please login again") { // 清除本地 token,導向登入頁 localStorage.removeItem('token'); window.location.href = '/login'; } } ``` --- ## 🔍 除錯與監控 ### 日誌位置: ``` ./logs/app.log ``` ### 重要日誌事件: - `Authentication successful for user: {email}` - 登入成功 - `Created session {id} for user {email}` - Session 創建 - `Authenticated user: {email} (ID: {id})` - JWT 驗證成功 - `Expired session {id} for user {email}` - Session 過期 - `Created task {task_id} for user {email}` - 任務創建 ### 資料庫查詢: ```sql -- 檢查用戶 SELECT * FROM tool_ocr_users WHERE email = 'user@example.com'; -- 檢查 sessions SELECT * FROM tool_ocr_sessions WHERE user_id = 1 ORDER BY created_at DESC; -- 檢查任務 SELECT * FROM tool_ocr_tasks WHERE user_id = 1 ORDER BY created_at DESC LIMIT 10; -- 統計 SELECT status, COUNT(*) FROM tool_ocr_tasks WHERE user_id = 1 GROUP BY status; ``` --- ## ✅ 總結 ### 已完成: - ✅ 完整的資料庫架構設計 (4 個新表) - ✅ 外部 API 認證服務整合 - ✅ 用戶 Session 管理系統 - ✅ 任務管理服務 (CRUD + 隔離) - ✅ RESTful API 端點 (認證 + 任務) - ✅ JWT 驗證依賴項 - ✅ 資料庫遷移腳本 - ✅ API Schema 定義 ### 待繼續: - ⏳ 前端認證服務 - ⏳ 前端任務歷史 UI - ⏳ 整合測試 - ⏳ 文檔更新 ### 技術債務: - ⚠️ Token 加密 (高優先級) - ⚠️ 完整 JWT 驗證 (高優先級) - ⚠️ Token 刷新機制 --- **實作完成日期**:2025-11-14 **實作人員**:Claude Code **審核狀態**:待用戶測試與審核