API Keys
API Key 用於認證 API 請求,讓外部系統與自動化腳本安全地存取平台資源。
基礎路由:
/api/v1/api-keys認證要求:所有 API Key 管理操作均需透過 Session Token 進行認證
端點概覽
| 方法 | 路徑 | 說明 | Rate Limit |
|---|---|---|---|
| POST | /api/v1/api-keys | 建立 API Key | 10 req/min |
| GET | /api/v1/api-keys | 查詢 API Key 列表 | 60 req/min |
| GET | /api/v1/api-keys/{id} | 取得單一 API Key | 60 req/min |
| PATCH | /api/v1/api-keys/{id} | 更新 API Key | 20 req/min |
| DELETE | /api/v1/api-keys/{id} | 撤銷 API Key | 10 req/min |
| POST | /api/v1/api-keys/{id}/rotate | 輪換 API Key | 5 req/min |
使用場景
- 系統整合:為外部系統建立 API Key 進行資料交換
- 自動化腳本:在 CI/CD 或排程任務中呼叫 API
- IoT 設備認證:為設備建立專屬 Device Key
- 第三方應用:讓合作夥伴以受限 Scope 存取指定資源
架構概覽
系統採用兩層分離的認證架構:
| 層級 | Guard | 用途 |
|---|---|---|
| 管理層 (API Key CRUD) | AuthGuard (Session Only) | 建立、查詢、更新、撤銷 API Key。僅接受 Session Token,API Key 本身無法管理自己 |
| 資源層 (業務 API) | HybridAuthGuard | 存取設備、任務、場景、告警、分析等資源。接受 Session Token 或 API Key |
💡 這個設計確保 API Key 的生命週期管理需要人工 Session 認證,防止自動化腳本惡意建立 Key;而業務操作可透過 API Key 完全自動化,無需使用者持續登入。
API Key 類型與預設 Scope
建立 API Key 時需指定 type,若未提供 scopes 則套用對應的預設 Scope。
| 類型 | type 值 | Key 前綴 | 適用場景 | 預設 Scope |
|---|---|---|---|---|
| Device | device | syncai_dev_ | IoT 設備認證 | device:read, device:heartbeat, device:sync, task:read |
| Personal | personal | syncai_pat_ | 個人存取、開發測試 | 完整業務 Scope(不含 billing/usage 寫入) |
| Service | service | syncai_svc_ | 服務間通訊、後端整合 | *(Full Access) |
| Third Party | third_party | syncai_3rd_ | 第三方系統整合 | 完整業務 Scope(不含 billing/usage 寫入) |
| Webhook | webhook | syncai_whk_ | Webhook 回呼驗證 | webhook:receive |
PERSONAL / THIRD_PARTY 預設 Scope
device:read, device:write
task:read, task:write
scene:read, scene:write
alert:read, alert:write
analytics:read, analytics:write
webhook:read, webhook:write
SERVICE 預設 Scope
* (Full Access — 等同所有 Scope)
DEVICE 預設 Scope
device:read, device:heartbeat, device:sync, task:read
Key 格式
syncai_<type-prefix>_<8-char-prefix>_<random-secret>
| 部分 | 說明 |
|---|---|
syncai_ | 固定平台前綴 |
<type-prefix> | 類型前綴:dev / pat / svc / 3rd / whk |
<8-char-prefix> | 前 8 碼,用於 UI 顯示與快速識別 |
<random-secret> | 64 字元隨機密鑰主體 |
範例:
syncai_svc_Ab3xKm9P_h7sYn2xQkL9mP4wR8vB6tJ3nF5dH1gC7aE0iU2oY9s
⚠️ 重要:Raw Key 只在建立時回傳一次。請立即儲存至安全位置,遺失後只能 Rotate(無法恢復)。
權限範圍 (Scopes)
Scope 驗證採 AND 邏輯 — endpoint 要求的所有 Scope 都必須存在於 Key 的 scopes 陣列中。
*(FULL_ACCESS) 可通過任何 Scope 檢查。
| Scope 值 | 說明 | 對應資源 |
|---|---|---|
device:read | 讀取設備列表與詳情 | GET /devices |
device:write | 建立/更新/刪除設備 | POST/PATCH/DELETE /devices |
device:heartbeat | 設備心跳(內部用) | Device Auth |
device:sync | 設備同步(內部用) | Device Auth |
task:read | 讀取分析任務 | GET /analysis/tasks |
task:write | 建立/控制分析任務 | POST/PATCH /analysis/tasks |
scene:read | 讀取場景設定 | GET /scenes |
scene:write | 建立/更新/刪除場景 | POST/PATCH/DELETE /scenes |
alert:read | 讀取告警記錄與規則 | GET /analysis/alerts |
alert:write | 建立/更新告警規則 | POST/PATCH /analysis/alerts |
analytics:read | 讀取分析結果、計費、使用量 | GET /analysis/insights, /billing, /usage |
analytics:write | (保留,目前未使用) | — |
webhook:read | 讀取 Webhook 設定 | GET /webhooks |
webhook:write | 建立/更新 Webhook | POST/PATCH /webhooks |
* | 所有 Scope(Service Key 專用) | 全部端點 |
建立 API Key
POST /api/v1/api-keys
請求參數
| 參數 | 型別 | 必填 | 範圍/限制 | 說明 |
|---|---|---|---|---|
name | String | ✅ | 1-100 字元 | 易讀名稱 |
type | ApiKeyType | ✅ | 見上方類型表 | Key 類型 |
description | String | ❌ | 最大 500 字元 | 說明 |
scopes | ApiKeyScope[] | ❌ | 見 Scope 表 | 自定義 Scope,省略則使用該類型預設值 |
deviceId | UUID | ❌ | - | DEVICE 類型必填 |
expiresInDays | Number | ❌ | 1-3650 | 有效天數,省略則永不過期 |
rateLimit | Number | ❌ | 1-10000 | per-Key 速率限制(req/min) |
allowedIps | String[] | ❌ | 最多 50 條 | IP 白名單(支援 CIDR) |
metadata | Object | ❌ | - | 自定義 metadata |
透過儀表板建立
- 登入 SaaS 儀表板
- 前往 帳號設定 → API Keys (
/account/api-keys) - 點擊 「建立 API Key」
- 填寫名稱、說明、權限範圍(Scopes)、過期時間等
- 確認建立後,系統會顯示完整的 API Key
透過 API 建立
範例:建立 Personal Access Token
curl -X POST $BASE_URL/api/v1/api-keys \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline Key",
"type": "personal",
"description": "GitHub Actions deployment pipeline",
"expiresInDays": 365
}'
範例:建立限制 Scope 的 Third-Party Key
curl -X POST $BASE_URL/api/v1/api-keys \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Grafana Read-Only Integration",
"type": "third_party",
"scopes": ["analytics:read", "alert:read"],
"rateLimit": 60,
"allowedIps": ["10.0.0.0/8"]
}'
範例:建立 Service Key(Full Access)
curl -X POST $BASE_URL/api/v1/api-keys \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Backend Microservice Key",
"type": "service",
"description": "Used by internal analysis orchestrator"
}'
回應範例 (201 Created)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"apiKey": "syncai_svc_Ab3xKm9P_h7sYn2xQkL9mP4wR8vB6tJ3nF5dH1gC7aE0iU2oY9s",
"prefix": "Ab3xKm9P",
"name": "Backend Microservice Key",
"type": "service",
"scopes": ["*"],
"createdAt": "2026-03-02T02:00:00.000Z",
"expiresAt": null,
"warning": "This API key will only be shown once. Please save it securely."
}
⚠️ 重要:API Key 只會顯示一次,請立即複製並妥善保存。建議存放於環境變數或密鑰管理系統(如 HashiCorp Vault、AWS Secrets Manager)。
Key 管理操作
查詢列表
GET /api/v1/api-keys
| Query 參數 | 類型 | 說明 |
|---|---|---|
page | Number | 頁碼,預設 1 |
limit | Number | 每頁筆數,預設 20 |
filters[type] | ApiKeyType | 依類型篩選 |
filters[isActive] | Boolean | 依啟用狀態篩選 |
filters[deviceId] | UUID | 依設備篩選 |
# List all active Service Keys
curl "$BASE_URL/api/v1/api-keys?filters[type]=service&filters[isActive]=true" \
-H "Authorization: Bearer $SESSION_TOKEN"
回應範例 (200 OK):
{
"data": [
{
"id": "550e8400-...",
"name": "Backend Microservice Key",
"type": "service",
"scopes": ["*"],
"keyPrefix": "Ab3xKm9P",
"isActive": true,
"lastUsedAt": "2026-03-02T01:30:00.000Z",
"usageCount": 1420,
"expiresAt": null,
"createdAt": "2026-03-01T00:00:00.000Z"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalRecords": 1,
"limit": 20
}
}
ℹ️ 回傳結果不包含 raw key,僅顯示
keyPrefix供識別用途。
取得單筆
GET /api/v1/api-keys/{id}
curl "$BASE_URL/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer $SESSION_TOKEN"
更新
PATCH /api/v1/api-keys/{id}
可更新欄位:name、description、scopes、isActive(停用/啟用)、rateLimit、allowedIps、metadata。
# Deactivate a Key
curl -X PATCH "$BASE_URL/api/v1/api-keys/550e8400-..." \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"isActive": false}'
# Reduce scopes and rate limit
curl -X PATCH "$BASE_URL/api/v1/api-keys/550e8400-..." \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scopes": ["analytics:read", "alert:read"],
"rateLimit": 30
}'
輪換 (Rotate)
POST /api/v1/api-keys/{id}/rotate
Rotate 會原子性地建立新 Key 並立即使舊 Key 失效。建議定期執行 Rotate 以最小化洩漏影響。
curl -X POST "$BASE_URL/api/v1/api-keys/550e8400-.../rotate" \
-H "Authorization: Bearer $SESSION_TOKEN"
回應格式同「建立 API Key」,含一次性顯示的新 raw key。
撤銷 (Delete)
DELETE /api/v1/api-keys/{id}
⚠️ 不可逆操作。撤銷後的 Key 立即失效,無法恢復。
curl -X DELETE "$BASE_URL/api/v1/api-keys/550e8400-..." \
-H "Authorization: Bearer $SESSION_TOKEN"
回應:204 No Content
使用 API Key 存取資源
取得 API Key 後,在 API 請求的 Authorization Header 中帶入(格式與 Session Token 相同,系統依 syncai_ 前綴自動識別):
# List devices using API Key
curl "$BASE_URL/api/v1/devices" \
-H "Authorization: Bearer syncai_svc_Ab3xKm9P_h7sYn2xQkL9..."
# Query analysis tasks
curl "$BASE_URL/api/v1/analysis/tasks" \
-H "Authorization: Bearer syncai_pat_Xm9pQr2K_..."
# Read billing info (requires analytics:read scope)
curl "$BASE_URL/api/v1/billing/monthly-quota" \
-H "Authorization: Bearer syncai_svc_Ab3xKm9P_..."
各資源的 Scope 要求
| 資源路由 | 讀取需要 | 寫入需要 |
|---|---|---|
/api/v1/devices | device:read | device:write |
/api/v1/analysis/tasks | task:read | task:write |
/api/v1/scenes | scene:read | scene:write |
/api/v1/analysis/alerts | alert:read | alert:write |
/api/v1/analysis/insights | analytics:read | — |
/api/v1/billing | analytics:read | — |
/api/v1/usage | analytics:read | — |
/api/v1/files | (無 scope 限制) | (無 scope 限制) |
安全最佳實踐
Key 儲存
- 永遠不要將 raw key 存入程式碼或 Git repository
- 使用環境變數(
.env)或 Secret Manager(AWS Secrets Manager、GCP Secret Manager) - 啟用
.gitignore排除.env檔案
最小權限原則
# ❌ Bad — full scope for a read-only use case
{
"type": "personal"
}
# ✅ Good — only grant needed scopes
{
"type": "third_party",
"scopes": ["analytics:read", "device:read"]
}
設定過期時間
| 場景 | 建議天數 |
|---|---|
| 短期整合任務 | 30 天 |
| CI/CD Keys | 90 天 |
| 長期服務 | 365 天 |
IP 白名單
可信賴的服務端加上 IP 限制,大幅降低 Key 洩漏的影響範圍:
{
"allowedIps": [
"10.0.0.0/8",
"203.0.113.45"
]
}
定期 Rotate
建議設定排程定期 Rotate,降低長期洩漏風險:
curl -X POST "$BASE_URL/api/v1/api-keys/$KEY_ID/rotate" \
-H "Authorization: Bearer $SESSION_TOKEN" \
| jq -r '.apiKey' # Extract new key and update to Secret Manager
安全機制
| 機制 | 說明 |
|---|---|
| IP 白名單 | 設置 allowedIps 後,僅白名單中的 IP/CIDR 可使用 |
| Rate Limit | 每分鐘滑動窗口限流 |
| Scope AND 邏輯 | 端點所需多個 scope 全部滿足才能存取 |
| Key 過期 | 超過設定天數自動失效 |
| 即時撤銷 | 在前端管理頁面可立即撤銷或停用 |
Rate Limiting 規則
使用 API Key 呼叫端點時,適用以下速率限制:
| 端點類型 | 限制 | 適用範圍 |
|---|---|---|
| 一般端點 | 60 req/min | 設備/場景/告警查詢 |
| 分析控制 | 30 req/min | 啟動/停止分析 |
| 資料寫入 | 30 req/min | 建立/更新資源 |
| 文件上傳 | 10 req/min | 圖片/文件上傳 |
| Webhook 測試 | 5 req/min | 測試 Webhook |
HTTP Headers
每次回應會包含以下 Rate Limit 資訊:
| Header | 說明 |
|---|---|
X-RateLimit-Limit | 該端點的速率限制 |
X-RateLimit-Remaining | 剩餘可用請求數 |
X-RateLimit-Reset | 限制重置時間 (Unix timestamp) |
Retry-After | 被限流時,建議重試秒數 |
錯誤碼速查
| HTTP 狀態碼 | 情境 |
|---|---|
401 Unauthorized | Session Token 無效或過期 |
403 Forbidden | 嘗試操作不屬於自己的 Key |
404 Not Found | Key ID 不存在 |
422 Unprocessable Entity | 請求 Body 驗證失敗(欄位格式或值不符) |
429 Too Many Requests | 超過端點速率限制 |