SCIM 自動佈建
用 SCIM 2.0 從 Microsoft Entra ID、Okta 等身分提供者自動同步組織人員與單位
SCIM(System for Cross-domain Identity Management)2.0 是同步使用者與群組的標準 REST API。啟用後,公司的身分提供者(IdP)會主動把員工與部門推送到 Hakila:新進、異動、停權都自動反映在組織的人員與單位上,不需要在 Console 手動維護。
同步方向是單向的:IdP 呼叫 Hakila,Hakila 不會反過來讀取 IdP。
同步的是什麼
| SCIM 資源 | Hakila | 說明 |
|---|---|---|
| User | 組織人員 | 可綁定 API Key,套用人員與單位的點數、流量限制 |
| Group | 組織單位 | Group 的 members 決定人員隸屬哪個單位 |
SCIM 同步的是「組織人員」,不是可登入 Console 的帳號。人員是 API Key 的歸屬對象,用來分攤預算與限流;要讓同事登入 Console,仍需從「組織人員管理 → 角色管理」邀請加入。
組織結構的概念請見 Workspace,預算的檢查順序請見用量與預算。
啟用
只有組織管理員可以設定,且組織須為 Enterprise 方案(見組織方案);Business 方案的組織呼叫 SCIM 端點一律回 401。
- Console → 組織管理 → 組織人員管理 → 成員管理,在「SCIM 自動佈建」區塊按 啟用並產生 Token。
- 複製畫面上的兩個值,貼到 IdP 的 SCIM 設定:
- SCIM 端點(Tenant URL):
https://api.hakila.ai/scim/v2 - Secret Token:
hkscim_開頭的字串
- SCIM 端點(Tenant URL):
- 在 IdP 指派要同步的使用者與群組,開始佈建。
Token 只在產生當下顯示一次,Hakila 只保存它的雜湊值;遺失時請重新產生。
- 重新產生 Token:舊 Token 立即失效,需同步更新 IdP 的設定。
- 停用:Token 失效,IdP 無法再同步;已佈建的人員與單位會保留。
每個組織的 Token 各自獨立,Hakila 以 Token 判斷要寫入哪個組織。卡片上會顯示最近一次同步時間,以及已佈建的人員與單位數。
設定身分提供者
- Entra 系統管理中心 → 企業應用程式 → 新增應用程式 → 建立您自己的應用程式,選「整合任何其他在資源庫中找不到的應用程式(非資源庫)」。
- 進入應用程式 → 佈建 → 佈建模式選 自動。
- 管理員認證:租用戶 URL 填
https://api.hakila.ai/scim/v2,祕密權杖填 Hakila 產生的 Token,按 測試連線。 - 對應:保留預設的使用者與群組對應即可。若想讓人員 ID 使用員工編號,把
employeeId對應到urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber。 - 使用者和群組:指派要同步的使用者,以及代表部門的群組。
- 回到佈建頁,把佈建狀態切為 開啟。
Entra 以 userName(預設為 UPN)比對既有人員,以 displayName 比對既有單位;佈建週期約 40 分鐘一次,可用「視需要佈建」立即同步單一使用者。
欄位對應
User
| SCIM 屬性 | Hakila 人員欄位 | 說明 |
|---|---|---|
userName | 登入名稱 | 必填,組織內不可重複 |
displayName → name.formatted → name.givenName + name.familyName | 姓名 | 依序取第一個有值的 |
emails(primary → work → 第一筆) | 沒有 emails 且 userName 像 Email 時,以 userName 當 Email | |
active | 啟用狀態 | 預設 true |
externalId | 外部 ID | IdP 端的識別碼 |
enterprise employeeNumber | 人員 ID | 只在建立時採用;沒有或已被使用時,改由 userName 產生(重複時加 -2、-3) |
groups 為唯讀,顯示人員目前所屬的單位;調整隸屬請透過 Group 的 members。
Group
| SCIM 屬性 | Hakila 單位欄位 | 說明 |
|---|---|---|
displayName | 單位名稱 | 必填;單位 ID 由名稱自動產生 |
externalId | 外部 ID | |
members | 隸屬於此單位的人員 | 只接受 User |
同步行為
新進人員。 IdP 建立 User 時,若組織內已有手動建立、尚未同步過的人員,且其 Email 與 userName 相同,Hakila 會直接接手這筆人員,保留原有的預算、流量限制與 API Key 綁定;否則建立新人員。導入 SCIM 前已手動建好的人員,只要 Email 一致就不會重複。
停權。 active 改為 false 後,指派給這位人員的 API Key 會立即無法呼叫,回傳 401。重新啟用後恢復。
刪除人員。 DELETE /Users/{id} 會刪除人員,效果與在 Console 刪除相同。多數 IdP 在取消指派時只會先停權(active = false),之後才刪除。
部門異動。 每位人員只隸屬一個單位:
- 把人員加入某個 Group,等於把他移到該單位;他已綁定的 API Key 也跟著改套用新單位的預算與限流。
- 從 Group 移除,人員會回到根組織。
- 同一人被加入多個已同步的 Group 時,以最後一次異動為準。建議只把代表部門的群組同步到 Hakila。
單位階層。 透過 SCIM 建立的單位一律放在第一層。需要上下層關係時,到「組織人員管理 → 組織階層管理」調整;之後 SCIM 更新同一單位不會改動它的上層。
刪除單位。 DELETE /Groups/{id} 只刪除該單位,底下的子單位與人員會移到上一層,不會一併刪除。這與在 Console 刪除單位不同(Console 會連同子單位與人員一起刪除)。
手動編輯。 SCIM 佈建的人員在 Console 仍可編輯,但姓名、Email、單位與啟用狀態可能在 IdP 下次同步時被覆寫,建議在 IdP 端調整。預算、流量限制與 API Key 綁定只在 Hakila 管理,不會被同步覆寫。
端點
所有端點都需要 Authorization: Bearer hkscim_…,回應的 Content-Type 為 application/scim+json。
| 方法 | 路徑 | 說明 |
|---|---|---|
GET | /scim/v2/Users | 列出人員,支援 filter、startIndex、count |
GET PUT PATCH DELETE | /scim/v2/Users/{id} | 讀取、取代、部分更新、刪除人員 |
POST | /scim/v2/Users | 建立人員 |
GET | /scim/v2/Groups | 列出單位,另支援 excludedAttributes=members |
GET PUT PATCH DELETE | /scim/v2/Groups/{id} | 讀取、取代、部分更新、刪除單位 |
POST | /scim/v2/Groups | 建立單位 |
GET | /scim/v2/ServiceProviderConfig | 支援的功能 |
GET | /scim/v2/ResourceTypes、/scim/v2/Schemas | 資源類型與 Schema |
分頁:startIndex 從 1 起算;count 預設 100,上限 1000。
Filter
只支援 eq 比較,可用 and 串接多個條件:
| 資源 | 可篩選的屬性 |
|---|---|
| Users | id、userName、externalId、displayName、emails(或 emails.value、emails[value eq "…"])、active |
| Groups | id、displayName、externalId、members(或 members.value) |
其他運算子(co、sw、or 等)回傳 400,scimType 為 invalidFilter。
PATCH
支援 add、replace、remove,path 可省略(以 value 物件一次更新多個屬性)。
- Users:
active、userName、displayName、name(含name.givenName等子屬性)、emails、externalId。active也接受字串"True"/"False"。 - Groups:
displayName、externalId、members。remove可用members[value eq "…"]指定成員;不指定成員時移出全部。
範例
確認 Token 可用:
curl "https://api.hakila.ai/scim/v2/Users?count=1" \
-H "Authorization: Bearer $HAKILA_SCIM_TOKEN"停權一位人員:
curl -X PATCH "https://api.hakila.ai/scim/v2/Users/{id}" \
-H "Authorization: Bearer $HAKILA_SCIM_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{ "op": "replace", "path": "active", "value": false }]
}'把人員加入單位:
curl -X PATCH "https://api.hakila.ai/scim/v2/Groups/{groupId}" \
-H "Authorization: Bearer $HAKILA_SCIM_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{ "op": "add", "path": "members", "value": [{ "value": "{userId}" }] }]
}'錯誤
錯誤回應採 SCIM 格式:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "409",
"detail": "userName alice@example.com 已存在",
"scimType": "uniqueness"
}| 狀態碼 | scimType | 情況 |
|---|---|---|
400 | invalidSyntax | 請求內容不是有效的 JSON,或 PatchOp 格式不正確 |
400 | invalidFilter | 不支援的 filter |
400 | invalidValue | 缺少 userName/displayName,或長度超過上限 |
401 | Token 無效、已重新產生,或組織已停用 SCIM | |
404 | 找不到該 User 或 Group(只能存取 Token 所屬組織的資料) | |
409 | uniqueness | userName 已被組織內其他人員使用 |