sfappAPI 文件

認證與金鑰

每個 /v1 端點都要帶金鑰。金鑰決定哪一家商店、哪一個應用程式、可以做哪些事。商店身分由金鑰推導,請求裡無從指定。

帶上 Bearer 標頭

金鑰放在 Authorization 標頭。

curl -sS https://api.sfec.cloud/v1/me \
  -H "Authorization: Bearer $SFAPP_KEY"

標頭格式是 Bearer、一個半形空白、金鑰本體。Bearer 大小寫不拘。前後多餘的空白、兩個以上的空白都回 401。查詢字串與請求內文不接受金鑰。

讀金鑰格式

sfapp_live_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
└────┬───┘ └──┬───┘ └──────────────┬──────────────┘
  固定前綴   key_id(8 碼)      祕密段(32 碼)
段落 內容 可否外流
sfapp_live_ 固定前綴
key_id 8 碼小寫英數,例 ab12cd34 可,適合寫進日誌
祕密段 32 碼英數,發放當下給一次

矽羽只保存整串金鑰的雜湊與 key_id,祕密段無法還原。金鑰遺失時撤銷舊的、重發一把新的。

日誌與錯誤回報記 key_id 就足以指認是哪一把。向矽羽申請撤銷時也用 key_id 指名。

對照權限與端點

一把金鑰帶固定的權限範圍(scope),發放後無法自行擴充。

權限範圍 對應端點
read_blog GET /v1/blog/postsGET /v1/blog/posts/{id}GET /v1/blog/categories
write_blog POST /v1/blog/postsPATCH /v1/blog/posts/{id}DELETE /v1/blog/posts/{id}POST /v1/blog/categories
publish_blog POST /v1/blog/posts/{id}/publishPOST /v1/blog/posts/{id}/unpublish
write_media POST /v1/media/imagesDELETE /v1/media/images/{id}
read_audit GET /v1/audit/events

write_blog 隱含 read_blogpublish_blog 要單獨申請,只有 write_blog 的金鑰寫得了草稿、動不了線上內容。

GET /v1/me 接受任一把有效金鑰。回應的 scopes 是這把金鑰展開隱含關係後的實際清單。

盯住到期日

金鑰預設 365 天到期。到期時間看 GET /v1/mekey_expires_at(ISO 8601 含時區),沒有設定到期日時是 null

{
  "scopes": ["read_blog", "write_blog"],
  "key_expires_at": "2027-09-14T00:00:00+08:00"
}

過期的金鑰回 401 unauthorized,回應與輸錯金鑰相同。到期前沒有預告。把 key_expires_at 讀進監控,到期前自行提醒換新。

輪替金鑰

同一個應用程式可以同時有兩把有效金鑰,換金鑰不必停機。輪替步驟:

  1. 向矽羽申請第二把金鑰,權限與舊的相同。
  2. 把新金鑰部署上線。
  3. GET /v1/audit/events 或自己的記錄確認新金鑰在用。
  4. 請矽羽撤銷舊金鑰。

先撤銷再換會讓整合在中間那段時間全部回 401。

輪替的時機:

  • 每年一次。
  • 負責串接的人員異動。
  • 金鑰可能外流。
  • 金鑰貼進共享空間。

撤銷金鑰

懷疑金鑰外流就聯絡矽羽撤銷。撤銷即時生效,被撤銷的金鑰之後一律回 401。撤銷不影響已寫入的資料,也不影響同一個應用程式的其他金鑰。

聯絡時提供 key_id,完整金鑰留在自己手上。

保管金鑰

  • 金鑰只放環境變數或密碼管理器,版本控制與設定檔裡留空。
  • 金鑰留在伺服器端。前端程式碼、瀏覽器、手機應用程式都在顧客拿得到的範圍內。
  • 交接用密碼管理器分享,不用聊天室、工單或截圖。
  • 一個用途一把金鑰:排程一把、代理程式一把、手動測試一把。出事時撤銷那一把。
  • 只需要讀的整合就只發 read_blog
  • 日誌與錯誤回報只記 key_id

分辨 401 與 403

401 代表金鑰無效,403 代表金鑰有效、權限不足。

401 unauthorized 403 insufficient_scope
常見原因 沒帶 Authorization、格式不符、金鑰打錯、已過期、已撤銷 金鑰缺少這支端點需要的權限範圍
處理動作 檢查標頭格式與金鑰是否仍有效 error.details 所列權限向矽羽申請
details 內容 不列欄位 每筆的 fieldscopeissuerequired: 加權限名
可否重試 否,原樣重送結果相同 否,原樣重送結果相同

403 的回應指名缺哪一個權限:

{
  "error": {
    "code": "insufficient_scope",
    "message": "這把金鑰的權限不足,請向矽羽申請 details 所列的 scope 後再試。",
    "details": [{ "field": "scope", "issue": "required: publish_blog" }],
    "request_id": "req_k4m2p7q9r3s5t8v6w2x4y7z3",
    "retryable": false
  }
}

錯誤外殼的欄位說明見請求慣例,完整錯誤碼見錯誤碼