8.3 KiB
8.3 KiB
認證與授權 API
📋 概述
Drama Ling 應用的認證與授權系統,支援傳統註冊登入和第三方社群登入(Apple ID、Google)。
🔐 JWT 認證機制
Token 結構
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "dramatic_learner",
"role": "user", // user, subscriber, admin
"subscription_status": "active", // active, inactive, trial
"iat": 1693920600,
"exp": 1693924200
}
Token 生命週期
- Access Token: 1小時有效期
- Refresh Token: 30天有效期
- Token 輪替: 每次刷新會產生新的 Refresh Token
- Token 黑名單: 登出時將 Token 加入黑名單
📌 API 端點
1. 用戶註冊
POST /api/v1/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123",
"username": "dramatic_learner",
"preferredLanguage": "en",
"nativeLanguage": "zh-TW"
}
回應範例
Response 201 Created
{
"success": true,
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"username": "dramatic_learner",
"email": "user@example.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440001",
"expiresIn": 3600,
"userRole": "user",
"isNewUser": true
},
"message": "User registered successfully",
"meta": {
"timestamp": "2024-09-07T12:00:00Z",
"requestId": "550e8400-e29b-41d4-a716-446655440002"
}
}
註冊驗證規則
- Email: 有效的電子郵件格式
- Password: 最少8位,包含大小寫字母和數字
- Username: 3-20字元,僅允許字母數字和底線
- Language: ISO 639-1 語言碼
錯誤處理
Response 400 Bad Request
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "註冊資料驗證失敗",
"details": {
"email": ["Email format is invalid"],
"password": ["Password must be at least 8 characters"],
"username": ["Username already exists"]
}
}
}
2. 用戶登入
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securePassword123",
"rememberMe": true
}
回應範例
Response 200 OK
{
"success": true,
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"username": "dramatic_learner",
"email": "user@example.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440001",
"expiresIn": 3600,
"userRole": "subscriber",
"subscriptionStatus": "active",
"lastLoginAt": "2024-09-07T12:00:00Z"
},
"message": "Login successful"
}
錯誤處理
Response 401 Unauthorized
{
"success": false,
"error": {
"code": "INVALID_CREDENTIALS",
"message": "Email或密碼錯誤",
"details": {
"attemptCount": 3,
"lockoutTime": null
}
}
}
3. Token 刷新
POST /api/v1/auth/refresh
Authorization: Bearer <refresh_token>
回應範例
Response 200 OK
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440001",
"expiresIn": 3600,
"tokenType": "Bearer"
},
"message": "Token refreshed successfully"
}
4. 用戶登出
POST /api/v1/auth/logout
Authorization: Bearer <access_token>
回應範例
Response 200 OK
{
"success": true,
"data": {
"loggedOut": true,
"tokenBlacklisted": true
},
"message": "Logout successful"
}
5. Apple ID 登入
POST /api/v1/auth/apple
Content-Type: application/json
{
"identityToken": "apple_identity_token",
"authorizationCode": "apple_authorization_code",
"userIdentifier": "apple_user_identifier",
"email": "user@privaterelay.appleid.com",
"fullName": {
"givenName": "John",
"familyName": "Doe"
}
}
回應範例
Response 200 OK
{
"success": true,
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"username": "apple_user_12345",
"email": "user@privaterelay.appleid.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440001",
"expiresIn": 3600,
"isNewUser": false,
"authProvider": "apple"
},
"message": "Apple login successful"
}
6. Google 登入
POST /api/v1/auth/google
Content-Type: application/json
{
"idToken": "google_id_token",
"accessToken": "google_access_token",
"serverAuthCode": "google_server_auth_code"
}
回應範例
Response 200 OK
{
"success": true,
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"username": "google_user_12345",
"email": "user@gmail.com",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "550e8400-e29b-41d4-a716-446655440001",
"expiresIn": 3600,
"isNewUser": true,
"authProvider": "google"
},
"message": "Google login successful"
}
🔒 權限控制
用戶角色
enum UserRole {
USER = "user", // 免費用戶
SUBSCRIBER = "subscriber", // 訂閱用戶
ADMIN = "admin" // 管理員
}
權限矩陣
| 功能 | 免費用戶 | 訂閱用戶 | 管理員 |
|---|---|---|---|
| 基礎對話練習 | 3次/日 | 無限制 | 無限制 |
| 高級對話功能 | ❌ | ✅ | ✅ |
| 詞彙複習 | 基礎 | 進階 | 完整 |
| 特殊任務 | 部分 | 完整 | 完整 |
| 數據分析 | 基礎 | 詳細 | 完整 |
| 管理功能 | ❌ | ❌ | ✅ |
權限檢查中介軟體
// API 權限檢查範例
app.use('/api/v1/dialogues/advanced', requireSubscription);
app.use('/api/v1/admin/*', requireAdminRole);
function requireSubscription(req, res, next) {
if (req.user.role !== 'subscriber' && req.user.role !== 'admin') {
return res.status(403).json({
success: false,
error: {
code: "SUBSCRIPTION_REQUIRED",
message: "此功能需要訂閱會員"
}
});
}
next();
}
⚡ 安全措施
密碼安全
- 加密: bcrypt + salt,成本因子 12
- 複雜度: 最少8位,大小寫字母+數字
- 重設: 臨時token,15分鐘有效期
防攻擊機制
- Rate Limiting: 每IP每分鐘5次登入嘗試
- 帳號鎖定: 5次失敗後鎖定15分鐘
- CSRF Protection: 所有POST請求需要CSRF token
- Input Sanitization: 所有輸入進行清理和驗證
Token 安全
- HTTPS Only: 所有認證相關請求強制HTTPS
- HttpOnly Cookies: Refresh token存放在HttpOnly cookie
- Token 黑名單: 登出和可疑活動時加入黑名單
- 定期輪替: 每24小時自動輪替長期token
🔧 錯誤碼
認證相關錯誤
| 錯誤碼 | HTTP狀態 | 描述 |
|---|---|---|
INVALID_CREDENTIALS |
401 | 登入憑證錯誤 |
TOKEN_EXPIRED |
401 | Token已過期 |
TOKEN_INVALID |
401 | Token無效或格式錯誤 |
TOKEN_BLACKLISTED |
401 | Token已被列入黑名單 |
ACCOUNT_LOCKED |
423 | 帳號因多次失敗嘗試被鎖定 |
EMAIL_EXISTS |
409 | 電子郵件已被註冊 |
USERNAME_EXISTS |
409 | 使用者名稱已存在 |
VALIDATION_ERROR |
400 | 輸入資料驗證失敗 |
SOCIAL_LOGIN_ERROR |
400 | 第三方登入驗證失敗 |
🧪 測試指南
測試用例
# 正常註冊流程測試
curl -X POST https://api.dramaling.com/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test123456","username":"testuser"}'
# 登入流程測試
curl -X POST https://api.dramaling.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test123456"}'
# Token刷新測試
curl -X POST https://api.dramaling.com/api/v1/auth/refresh \
-H "Authorization: Bearer <refresh_token>"
安全測試
- 暴力破解測試: 驗證帳號鎖定機制
- Token洩漏測試: 驗證Token黑名單機制
- 權限繞過測試: 驗證角色權限控制
- 資料注入測試: 驗證輸入清理機制