KeycloakのSCIM対応は、2026年10月公開の26.8.0で正式サポートの機能になりました。外部のID管理システムやEntra IDから、Keycloakの利用者とグループを標準のSCIM 2.0で作成・更新・無効化できます。本記事の解説対象は、2026年11月5日公開の最新版26.8.2です。このバージョンを前提に、SCIM APIで何ができて何ができないか、レルムでの有効化とトークンの設定、Entra IDからの自動プロビジョニング、本番で引っかかる制約、そして本体に無い「KeycloakからSaaSへ送る側」の作り方までをコード付きで解説します。SCIMという規格そのものの仕組みは、SCIMとは?ID自動プロビジョニングの仕組みとエンドポイント実装で整理しています。
まとめ:Keycloak SCIMは26.8で正式機能となり受け側だけ本体で動く
26.8以降なら、拡張を入れずにKeycloak本体のSCIM APIを使えます。サーバーの機能フラグscim-apiは既定で有効なので、やることはレルムのSCIM APIをONにし、サービスアカウントのクライアントにmanage-usersとaudienceを設定するだけです。Entra IDからは、OAuth2 Client Credentials GrantでKeycloakのトークンエンドポイントを指定してつなぎます。
本体が持つのは、外部から利用者を受け取る側だけです。Keycloakの利用者をSlackや業務SaaSへ配る側は未搭載なので、拡張か自前の同期ジョブで補います。Bulk・ソート・パスワード変更は使えず、管理者ロールを持つ利用者はSCIMから変更できません。この制約で困らない構成なら本体の機能で十分で、組織(Organizations)単位で受け付けたい場合に限って拡張を検討します。
KeycloakのSCIM対応状況:26.6の実験版から26.8の正式機能までの範囲
「KeycloakはSCIMに対応していない」という説明は、2026年春までの情報です。ここでは版ごとの扱いと、本体の機能が受け持つ範囲を確認します。Keycloakそのものの位置づけはKeycloakとは?Oktaとの違いと連携手順を参照してください。
26.6実験・26.7プレビュー・26.8正式サポートと既定で有効な機能フラグ
Keycloak公式ブログのSCIM Realm API as an Experimental Feature(2026年4月10日)が、26.6で実験的機能として入ったことを告知しました。リリースノートによると、26.7.0でプレビューに上がり、26.8.0で「preview to supported」に昇格しています。
| 版 | 扱い | サーバー側の有効化 |
|---|---|---|
| 26.6 | 実験的機能 | --features=scim-apiが必要 |
| 26.7 | プレビュー | --features=scim-apiが必要 |
| 26.8 | 正式サポート | 既定で有効 |
26.8.2のProfile.javaではSCIM_APIの種別がDEFAULTです。Enabling and disabling featuresでも、scim-api:v1は既定で有効な機能の一覧に載っています。プレビュー時点の解説記事にある「既定で無効」は、26.8では当てはまりません。使わないなら--features-disabled=scim-apiで止められます。
本体のSCIMは外部から利用者を受け取る側だけで送り出し側は未搭載
SCIMには、利用者を送る「クライアント」と、受け取って保存する「サービスプロバイダー」の2つの役割があります。Keycloak本体が実装したのは、サービスプロバイダー側です。人事システムやEntra IDといった外部が、/realms/<レルム名>/scim/v2に向けて利用者とグループを書き込みます。
Keycloakで作った利用者を、各SaaSのSCIMエンドポイントへ配る機能はありません。2026年2月の公式ブログは、サービスプロバイダー側を優先し、クライアント側は検討中と書いています。正式化を報告したissue #46229も、対象はSCIMサービスです。「Keycloakを起点にSaaSのアカウントを自動で作る」構成は、後半の章で扱う方法で補う必要があります。
ServiceProviderConfigによるSCIM非対応機能の確認
何に対応しているかは、/ServiceProviderConfigの応答で確かめられます。26.8.2の管理ガイドの該当節に載っている応答の値は次のとおりです。
| 項目 | 値 | 運用への影響 |
|---|---|---|
| patch | 対応 | 差分更新とメンバー追加ができる |
| filter | 対応・maxResults 100 | 1回の一覧は100件まで |
| bulk | 非対応 | 1件ずつ送る |
| sort | 非対応 | sortByは受け付けるが無視 |
| etag | 非対応 | If-Matchによる競合検出なし |
| changePassword | 非対応 | パスワードはSCIMで設定不可 |
パスワードを送れないので、SCIMで作った利用者は、外部IdPでのログインか、Keycloakが送るメールでの初回設定と組み合わせて使います。対応するリソースはUserとGroupの2つで、スキーマはコアのUserとGroup、Enterprise Userの拡張です。
KeycloakでSCIM APIを有効にしてcurlで利用者を作るまでの設定手順
ここからは、レルム名をcorp、Keycloakの公開URLをhttps://auth.example.comとして進めます。管理CLIのkcadm.shにはログイン済みとします。管理APIの基本操作はKeycloak APIの使い方で解説しました。
scimApiEnabledとサービスアカウントのクライアントをkcadm.shで作る
サーバーで機能が有効でも、レルムごとのトグルはOFFのままです。Server Administration Guideの「Managing users and groups through SCIM」の章は、管理コンソールのRealm settingsにあるSCIM APIの切り替えを案内しています。CLIでは、レルムのscimApiEnabledを書き換えます。
# 1. レルムで SCIM API を有効にする
kcadm.sh update realms/corp -s scimApiEnabled=true
# 2. client credentials で使う機密クライアントを作る(Full Scope は OFF)
CID=$(kcadm.sh create clients -r corp -i \
-s clientId=scim-client -s publicClient=false \
-s serviceAccountsEnabled=true -s standardFlowEnabled=false \
-s fullScopeAllowed=false)
# 3. サービスアカウントに realm-management の manage-users を付ける
kcadm.sh add-roles -r corp --uusername service-account-scim-client \
--cclientid realm-management --rolename manage-users
# 4. 同じロールをクライアントのスコープにも入れる
RM_ID=$(kcadm.sh get clients -r corp -q clientId=realm-management \
--fields id --format csv --noquotes)
ROLE=$(kcadm.sh get "clients/$RM_ID/roles/manage-users" -r corp)
kcadm.sh create "clients/$CID/scope-mappings/clients/$RM_ID" -r corp -b "[$ROLE]"
# 5. クライアントシークレットを確認する
kcadm.sh get "clients/$CID/client-secret" -r corp
アクセス権の節は、Full Scope Allowedを非推奨とし、OFFにしたうえでスコープとサービスアカウントの両方へロールを付けるよう書いています。トークンに載るのは両者の共通部分です。片方だけだと権限不足になります。読み取りだけの連携なら、manage-usersの代わりにview-usersとquery-users、query-groupsを付けます。
audienceにSCIMのベースURLを入れないとトークンが401で拒否される
同じ節によると、SCIM APIはアクセストークンのaudにSCIMのベースURLが入っているかを検証し、無ければ401 Unauthorizedを返します。ロールを正しく付けても、audienceが無いトークンでは通りません。Audienceマッパーをクライアントに足します。
kcadm.sh create "clients/$CID/protocol-mappers/models" -r corp \
-s name=scim-audience -s protocol=openid-connect \
-s protocolMapper=oidc-audience-mapper \
-s 'config."included.custom.audience"=https://auth.example.com/realms/corp/scim/v2' \
-s 'config."access.token.claim"=true'
値はスキーム・ホスト・ポート・パスまで完全一致です。リバースプロキシの後ろで動かしている場合は、内部のURLではなく、hostname設定で決めた公開URLを書きます。
トークン取得からUsersとGroups作成までのcurl実行例
トークンを取り、利用者とグループを1つずつ作って、グループにメンバーを足します。エンドポイントと本文の形は利用者管理の節に合わせました。
KC=https://auth.example.com/realms/corp
SCIM=$KC/scim/v2
TOKEN=$(curl -s -X POST "$KC/protocol/openid-connect/token" \
-d grant_type=client_credentials -d client_id=scim-client \
-d client_secret="$SCIM_CLIENT_SECRET" | jq -r .access_token)
# 利用者を作る(userName は必須。成功すると 201 Created)
USER_ID=$(curl -s -X POST "$SCIM/Users" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/scim+json" \
-d '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName":"t.yamada","name":{"givenName":"Taro","familyName":"Yamada"},
"active":true}' | jq -r .id)
# グループを作り、PATCH でメンバーを足す(POST に members を含めると 400)
GROUP_ID=$(curl -s -X POST "$SCIM/Groups" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/scim+json" \
-d '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"sales"}' \
| jq -r .id)
curl -s -X PATCH "$SCIM/Groups/$GROUP_ID" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/scim+json" \
-d "{\"schemas\":[\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],
\"Operations\":[{\"op\":\"add\",\"path\":\"members\",\"value\":\"$USER_ID\"}]}"
同じuserNameが既にあれば409 Conflictが返ります。グループのメンバーは作成時にもPUTでも渡せず、PATCHだけで変える仕様です。1回のPATCHに入れられる操作は100件までなので、大きなグループを移すときは分割して送ります。
Entra IDからKeycloakへ自動プロビジョニングする接続設定と属性の対応
典型的な使い方は、Entra IDを社員台帳の正本にし、アプリに割り当てた社員だけをKeycloakへ自動で作る構成です。ログインもEntra IDに任せるなら、Entra IDを外部IdPにするSAMLの設定と組み合わせます。
Entra IDのクライアント資格情報認証とTenant URLの設定
Microsoft LearnのSCIMエンドポイントのチュートリアル(2026年9月15日更新)によると、プロビジョニングの認証方式でOAuth2 Client Credentials Grantを選ぶと、4つの値を入力します。
| Entra IDの入力欄 | Keycloak側で入れる値 |
|---|---|
| Tenant URL | SCIMのベースURL |
| Token Endpoint | レルムのトークンエンドポイント |
| Client Identifier | scim-client |
| Client Secret | 前の章で確認したシークレット |
この例では、Tenant URLがhttps://auth.example.com/realms/corp/scim/v2、Token Endpointがhttps://auth.example.com/realms/corp/protocol/openid-connect/tokenになります。
同じページは、新しい連携では長期のベアラートークンを受け付けない方針も示しています。Keycloakのアクセストークンは数分で切れるので、Secret Token欄に固定のトークンを貼る方式は使えません。Client Credentials Grantを選びます。
externalIdとEnterprise Userの属性マッピング設定
Microsoftのチュートリアルは、Entra IDが既定でuserNameとexternalIdで利用者を照会すると書いています。一方、KeycloakのexternalIdは既定では写されず、User Profileに属性を足して対応づける設定が必要です。社員番号や部署もEnterprise Userの属性として同じ方法で写します。
# User Profile の設定を取り出し、attributes 配列に次の 2 要素を足して戻す
kcadm.sh get users/profile -r corp > profile.json
{
"name": "scimExternalId",
"displayName": "SCIM externalId",
"permissions": { "view": ["admin"], "edit": ["admin"] },
"annotations": { "kc.scim.schema.attribute": "externalId" }
},
{
"name": "employeeNumber",
"displayName": "社員番号",
"permissions": { "view": ["admin"], "edit": ["admin"] },
"annotations": {
"kc.scim.schema.attribute": "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:employeeNumber"
}
}
kcadm.sh update users/profile -r corp -f profile.json
属性の対応づけの節によると、adminのview権限が無い属性はSCIMの応答に出ず、edit権限が無ければ書き込みは黙って無視されます。電話番号(phoneNumbers)と住所(addresses)はコアのスキーマでは写せません。メールも1件だけ保存され、2件目以降は捨てられます。
約40分ごとの同期と割り当て解除時のactive=falseによる無効化の流れ
Entra IDの同期は、初回の全件処理の後、およそ40分ごとに回ります。同じチュートリアルの要件表には、active=falseによる論理削除とactive=trueでの復元への対応が挙がっています。Entra IDが無効化として送るのは、activeをfalseにするPATCHです。Keycloakではactiveがenabledに対応するので、利用者は無効になり、ログインできなくなります。
40分の遅れが許されない退職処理では、Entra ID側でサインインを止めるのと同時に、Keycloakの管理画面で該当者のセッションも削除します。SCIMの反映を待つ間も、既存のセッションは残るからです。
本番運用で詰まる制約:管理者保護・LDAP連携・1回100件の上限
手順どおりに設定しても、特定の利用者だけ更新できない、一覧に出てこないという症状が起きます。原因の多くは仕様上の制約です。
管理者ロールを持つ利用者とグループへの書き込みを403で拒否する保護
管理リソースの保護の節によると、realm-managementなどの管理ロールを持つ利用者は、SCIMからのPUT・PATCH・DELETEが403 Forbiddenになります。GETでもidとuserNameだけの最小限の表現しか返りません。グループ経由やコンポジットロールで持っている場合も対象です。
管理ロールを持つグループと、その子グループへのメンバー追加も拒否されます。SCIMのクライアントが乗っ取られても管理者を消せないようにする仕組みなので、外す設定はありません。情シスの担当者をEntra IDからの同期対象に入れるなら、Keycloakの管理権限は同期しない別アカウントに持たせます。
LDAP連携の利用者が検索に出ない条件と1回100件・フィルタ2,048文字の上限
Keycloakの/Usersが返すのは、Keycloakのデータベースにある利用者だけです。LDAP連携でImport UsersをOFFにした利用者は検索に出ません。グループマッパーをLDAP_ONLYにした構成では、groups.valueでの絞り込みが空になるとフィルタの節に書かれています。ADを正本にしたままSCIMでも書き込む構成は、台帳が2つになるので組みません。
一覧と検索は、countを省略しても101以上を指定しても1回100件で止まります。startIndexを100ずつ進めて全件を取ります。フィルタ式は2,048文字まで、括弧の入れ子は10段までです。sortByは受け付けても無視されるので、並び順に頼る処理は書けません。
KeycloakからSaaSへ送るSCIMクライアントを用意する方法と選び分け
「Keycloakに社員を登録したら、SlackやGitHubにも自動でアカウントができる」構成は、本体だけでは組めません。補う方法は、拡張を入れるか、自前の同期ジョブを置くかの2つです。そもそも送り元をKeycloakにしない選択肢もあります。
イベントリスナー型の拡張mitodl/keycloak-scimで変更を即時に送る構成
mitodl/keycloak-scimは、Keycloakの変更イベントを拾ってSaaSへSCIMで送る拡張です。User FederationにSCIMの送り先を登録し、Events設定のEvent Listenersにscimを足して使います。READMEは、イベントリスナーが取り消せない仕組みのため、送り先が落ちていると厳密な整合は保てないと明記しています。
2026年11月時点で、リポジトリへの更新は続いていますが、GitHubのReleasesにある最新は2024年9月です。26.8で動くかは自分で検証する前提になります。採用する場合も、取りこぼしを拾う定期の全件同期を必ず併用します。
KeycloakのSCIM APIを読みSaaSとの差分を直す突き合わせジョブ
送る側を自前で書くなら、変更イベントを追うより、定期的に両者を突き合わせる方式が壊れにくくなります。Keycloakの一覧も同じSCIMで読めるので、送り先がSCIM対応なら、比較に使うデータは同じ形式のままで扱える点が利点です。次の例は、Keycloakで無効か存在しない利用者を、SaaS側で無効にします。
# reconcile.py:Keycloak を正本に、SaaS 側の不要なアカウントを無効にする
import os
import requests
KC_SCIM = os.environ["KC_SCIM"] # https://auth.example.com/realms/corp/scim/v2
SAAS_SCIM = os.environ["SAAS_SCIM"] # 送り先 SaaS の SCIM ベース URL
CT = "application/scim+json"
def kc_token():
r = requests.post(os.environ["KC_TOKEN_URL"], timeout=30, data={
"grant_type": "client_credentials",
"client_id": os.environ["KC_CLIENT_ID"],
"client_secret": os.environ["KC_CLIENT_SECRET"]})
r.raise_for_status()
return r.json()["access_token"]
def list_users(base, headers):
users, start = [], 1
while True:
r = requests.get(f"{base}/Users", headers=headers, timeout=30,
params={"startIndex": start, "count": 100})
r.raise_for_status()
page = r.json().get("Resources", [])
users += page
start += len(page)
if not page or start > r.json()["totalResults"]:
return users
kc_h = {"Authorization": f"Bearer {kc_token()}", "Accept": CT}
# 管理者は最小表現で返り active が無いので、無い場合は有効とみなす
keep = {u["userName"] for u in list_users(KC_SCIM, kc_h) if u.get("active", True)}
saas_h = {"Authorization": f"Bearer {os.environ['SAAS_TOKEN']}", "Content-Type": CT}
for u in list_users(SAAS_SCIM, saas_h):
if u.get("active") and u["userName"] not in keep:
op = {"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{"op": "replace", "path": "active", "value": False}]}
requests.patch(f"{SAAS_SCIM}/Users/{u['id']}", json=op,
headers=saas_h, timeout=30).raise_for_status()
print("無効化:", u["userName"])
前の章の管理者保護のため、管理ロールを持つ利用者はactiveを含まない形で返ります。u.get("active")で判定すると管理者がSaaSから外れるので、このコードで指定している既定値はTrueです。読むだけのジョブなので、Keycloak側のクライアントにはview-usersとquery-usersだけを付けます。
Entra IDや人事システムが既に各SaaSへSCIMで送れるなら、Keycloakを経由させる理由はありません。送り元を1つにまとめたい目的がIDライフサイクルの管理なら、IGAとはで整理した専用製品の領域です。
Keycloak SCIMを採用する条件と拡張・IDaaSを選ぶ場面の判断基準
本体のSCIM APIで済むか、拡張やIDaaSに寄せるかは、送り元の数と組織の単位で決まります。
本体のSCIM APIで足りる条件:26.8以降・レルム単位・送り元が1つ
Keycloakが26.8以降で、Entra IDや人事システムなど送り元が1つ、レルム単位で利用者を受け取れば済むなら、本体の機能を使います。拡張の版追従が要らず、修正の取り込みに必要なのはKeycloak本体の更新だけです。26.6や26.7のまま使う場合は、プレビュー以前の機能を本番に置くことになるので、先にKeycloakの脆弱性対応の手順に沿って26.8系へ上げます。
Entra IDからの受け入れ設計、既存の利用者とのひも付け、退職処理の手順づくりまでをまとめて任せたい場合は、一創の認証基盤・ID管理システム開発で支援しています。
Metatavuの拡張やIDaaSに寄せる条件:組織単位の受付と送り出し
取引先の会社ごとにSCIMの受け口を分けたいB2B SaaSでは、本体の機能は合いません。組織グループのメンバーはSCIMから変更できず、Organization APIを使う仕様です。Metatavu/keycloak-scim-serverは組織単位のエンドポイントを持ちますが、READMEの検証済みの版は26.6.3までで、パスも本体と同じ/scim/v2です。26.8で併用する場合は、本体のscim-apiを止めたうえでの動作確認が要ります。
顧客企業ごとのSSOとSCIMを短期間で用意したいなら、WorkOSのようなSaaSに外出しするほうが、Keycloakに拡張を積むより保守の手間が減ります。IDaaS全体の比べ方はIDaaSとはにまとめました。
KeycloakのSCIM APIとEntra ID連携でよく受ける質問と回答
KeycloakのSCIMを設定する担当者から出やすい質問をまとめました。
SCIM APIの呼び出しが401になる場合はどこを確認すればよいですか?
最初の確認対象は、アクセストークンに含まれるaudienceです。アクセストークンをデコードし、audにSCIMのベースURLがスキームとポートまで同じ形で入っているかを見ます。次に、クライアントがconfidentialでサービスアカウントが有効か、公開クライアントになっていないかを確認します。公開クライアントはSCIMを呼べません。ロールが足りない場合は401ではなく403になるので、エラーの番号で原因の層を切り分けられます。
SCIMで作った利用者にパスワードを設定できますか?
できません。changePasswordは非対応で、password属性もSCIMでは書き込めません。Entra IDなど外部IdPでログインさせる構成なら、Keycloakにパスワードを持たせる必要はない設計です。Keycloakのパスワードでログインさせる場合は、Admin REST APIでUPDATE_PASSWORDの必須アクションを付けてメールを送るか、初回ログイン時に設定させます。
SCIMのDELETEとactive=falseはKeycloakでどう違いますか?
activeをfalseにするPATCHは利用者を無効にするだけで、行は残ります。DELETEは利用者そのものを消し、204 No Contentを返します。Entra IDの無効化はPATCHで届くので、監査のために記録を残したい組織では、Keycloak側で削除を急ぐ必要はありません。
26.7以前のKeycloakでSCIMを使う方法はありますか?
26.6と26.7では、起動時に--features=scim-apiを付ければ本体の機能を試せます。ただし実験的機能とプレビューなので、本番では26.8系への更新を先に行います。26.5以前は本体に機能が無く、拡張が必要です。古くから知られるCaptain-P-Goldfish/scim-for-keycloakはREADMEでEnd of Lifeとされているので、新しく入れる対象にはしません。
SCIMの規格に沿っているかはどう確かめればよいですか?
Keycloak公式ブログに記載されている開発時の確認ツールは、MicrosoftのSCIM Validatorです。自社のクライアントから送る場合は、RFC 7644のPATCHとフィルタの書式に沿っているかを、Keycloakの/Schemasと/ResourceTypesの応答と突き合わせます。最新の26.8.2は2026年11月5日公開のリリースで、SCIMの挙動は修正版でも変わるため、更新の前後で同じリクエストを流して比べます。
関連記事
- SCIMとは?ID自動プロビジョニングの仕組みとエンドポイント実装を解説:SCIMの規格と自前でエンドポイントを作る方法です。
- Keycloakとは?Oktaとの違いと連携手順|Auth0・Cognito比較で導入を判断【2026年10月】:Keycloakの機能と他製品との違いを確認できます。
- Keycloak APIの使い方:Admin REST APIでユーザー作成とロール付与をcurlで行う手順:SCIMで扱えないパスワードやロールの操作です。
- Keycloak LDAP連携:Active Directoryをつなぐ設定・同期・グループマッパー:ADを正本にする場合の同期の組み方です。
- Keycloak SAML設定:Entra IDを外部IdPにする手順と属性マッパー:SCIMで作った利用者をEntra IDでログインさせる構成です。