セキュリティ

Keycloak APIの使い方:Admin REST APIでユーザー作成とロール付与をcurlで行う手順

Keycloak APIの使い方:Admin REST APIでユーザー作成とロール付与をcurlで行う手順

Keycloakを自社の会員DBや人事システムとつなぐとき、管理コンソールの画面操作を自動化する入口になるのがAdmin REST APIです。本記事では、2026年11月5日時点の最新版Keycloak 26.8.1を前提に、サービスアカウントを持つクライアントの作り方、必要な管理ロールの選び方、client_credentialsでのトークン取得、ユーザーの作成・検索・ロール付与・無効化までを、そのまま動かせるcurlのコマンド例で解説します。つまずきやすい401・403・409の切り分けと、APIで連携を自作すべきかの判断基準も扱います。

まとめ:Keycloak APIはサービスアカウントのトークンで管理APIを呼ぶ

Keycloakの管理操作は、/admin/realms/{realm}配下のAdmin REST APIで呼べます。管理コンソールでできる操作はすべてAPIにもあり、2026年11月時点の公式OpenAPI定義には274のパスが載っています。

アプリから呼ぶなら、管理者のパスワードではなく、サービスアカウントを有効にしたクライアントのトークンを使います。ロールは対象レルムのrealm-managementから、必要なものだけを渡す設計です。ユーザーの作成・検索・更新はmanage-usersで足ります。ロールを名前で引く処理を入れるならview-realmも要ります。

数百人規模までの会員同期や入退社の反映なら、APIを直接呼ぶ実装で十分に回ります。既存のLDAPやActive Directoryが正本なら、APIで同期を書くよりUser Federationで読み込む構成を先に検討してください。

Keycloak APIの全体像:認証用OIDCとAdmin REST APIの役割分担

「Keycloak API」と呼ばれるものは、パスの先頭で2系統に分かれます。どちらを呼ぶのかを先に決めないと、トークンの取り方もロール設計も定まりません。製品そのものの機能や他製品との違いはKeycloakとは?Oktaとの違いと連携手順で扱っています。

/realms配下のトークン発行と/admin/realms配下の管理操作の違い

/realms/{realm}/protocol/openid-connect/配下は、ログインやトークン発行を担う標準のエンドポイントに当たります。アプリの利用者が認証するときに使い、仕様はOAuth 2.0とOpenID Connectに従います。

/admin/realms/{realm}/配下はKeycloak独自の管理APIです。公式のServer Developer Guideは、管理コンソールが持つ機能をすべて備えると説明しています。ユーザーやロールを登録・変更するのはこちらで、呼ぶには管理ロールを持つアクセストークンが要ります。

系統 パスの例 主な用途 必要な権限
OIDCエンドポイント myappのトークン発行用パス ログイン・トークン発行 クライアントの資格情報
Admin REST API myappのユーザー管理用パス ユーザー・ロール・クライアントの管理 realm-managementのロール

表のパス例は、OIDC側が/realms/myapp/protocol/openid-connect/token、管理API側が/admin/realms/myapp/usersです。本記事が扱うのは後者です。トークンは前者で取り、後者にBearerとして渡す、という順に動きます。

公式リファレンス26.8.1で確認できるエンドポイントの探し方

エンドポイントの正本はKeycloak Admin REST APIのリファレンスです。同じ場所にopenapi.jsonがあり、2026年11月5日に取得した定義には274のパスがありました。リファレンスのHTMLは長いので、OpenAPI定義をjqで引くほうが速く探せます。

読むときの注意が2つあります。パスの{client-id}はクライアントの内部ID(UUID)で、画面で入力するclientIdではありません。ロール関連の{role-name}は名前で、IDではありません。リファレンスにも「not clientId!」「not id!」と明記されており、取り違えると404になります。

管理APIを呼ぶトークンの取得:admin-cliとサービスアカウントの使い分け

トークンの取り方は、手元で試す場合とアプリに組み込む場合で分けます。結論から言うと、アプリには管理者のパスワードを持たせず、レルムごとのサービスアカウントを使います。

admin-cliとパスワードグラントで試す方法と有効期限1分の制約

公式ガイドの最初の例は、masterレルムの管理者でadmin-cliクライアントからパスワードグラントでトークンを取る方法です。ガイドには、このトークンは既定で1分で切れると書かれています。

動作確認には手軽です。ただし管理者のパスワードをスクリプトに書くことになり、そのトークンは全レルムを操作できます。バッチや本番のアプリに持ち込む方法ではありません。手元で数回叩いて挙動を見る用途に限ってください。

サービスアカウントを有効にしたクライアントをmyappレルムに作る設定

アプリ用には、管理したいレルム(以下myapp)にAPI専用のクライアントを作ります。管理ガイドのUsing a service accountに沿った手順は次のとおりです。

  1. 管理コンソールでmyappレルムを選び、Clientsからuser-syncという名前のOpenID Connectクライアントを作る
  2. SettingsでClient authenticationをOnにし、Service account rolesにチェックを入れる。Standard flowはオフにする
  3. CredentialsタブでClient Secretを控える
  4. Client scopesタブで専用スコープ(user-sync-dedicated)を開き、Scopeタブで必要なロールのスコープを割り当てる
  5. Service account rolesタブで、realm-managementの必要なロールを割り当てる

手順4と5は両方必要です。トークンに入るロールは、スコープの割り当てとサービスアカウントのロールの交差になるからです。26.8系の管理ガイドは「Full Scope Allowed」を非推奨とし、オンにせず必要なスコープを明示するよう求めています。

manage-usersとview-realmに絞るユーザー連携用のロール設計

各レルムにはrealm-managementという組み込みクライアントがあり、管理権限はそのクライアントロールで表されます。管理ガイドのDedicated realm admin consolesには、manage-usersやview-realm、全権を束ねたrealm-adminを含む22個が並びます。

ユーザー連携に要るのは2つです。manage-usersがあればユーザーの作成・検索・更新・削除が通ります。ソースのUserPermissionsは、manage-usersかview-usersを持てば閲覧可、閲覧できれば検索も可と判定しています。

見落としやすいのはロールの参照です。GET /roles/{role-name}は、26.8.1のRealmPermissionsでview-realmかmanage-realmを要求します。manage-usersだけでは403になるため、view-realmの追加が必要です。realm-adminを丸ごと渡すと、漏れたときにレルム設定まで書き換えられます。

client_credentialsでアクセストークンを取得するcurlコマンド

トークンはmyappレルムのトークンエンドポイントへgrant_type=client_credentialsで取りに行きます。管理ガイドのとおり、資格情報はBasic認証のヘッダで送れます。以降の例はjqがある前提です。

KC=http://localhost:8080
REALM=myapp
CLIENT_ID=user-sync
CLIENT_SECRET='CredentialsタブのClient Secret'

TOKEN=$(curl -s -u "${CLIENT_ID}:${CLIENT_SECRET}" \
  -d grant_type=client_credentials \
  "${KC}/realms/${REALM}/protocol/openid-connect/token" | jq -r .access_token)

echo "${TOKEN}" | cut -c1-20

応答のexpires_inが有効秒数です。この有効秒数を決める設定は、レルムのAccess Token Lifespanです。このグラントではリフレッシュトークンが既定で発行されないため、切れたら同じ要求で取り直します。トークンの中身はJWTの構造どおりで、resource_access.realm-management.rolesに割り当てたロールが入っているかで設定を確かめられます。

Admin REST APIでのユーザー操作:作成・検索・ロール付与・無効化のcurl例

ここからは取得したTOKENを使い、入社から退職までの一連の操作を順に呼びます。myappレルムにapp-userというレルムロールがある前提です。

POST /usersで初期パスワード付きのユーザーを作成する手順

ユーザーの作成はPOST /admin/realms/{realm}/usersです。credentialsにtemporary: trueのパスワードを入れると、初回ログインで変更を求められます。メールアドレスは環境変数NEW_USER_EMAILに入れてから実行してください。

BODY=$(jq -n --arg email "${NEW_USER_EMAIL}" '{
  username: "taro.yamada",
  email: $email,
  firstName: "Taro",
  lastName: "Yamada",
  enabled: true,
  emailVerified: false,
  credentials: [{type: "password", value: "Temp-Pass-2026", temporary: true}]
}')

curl -s -i -X POST "${KC}/admin/realms/${REALM}/users" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d "${BODY}" | grep -i -E '^(HTTP|location)'

成功すると201が返り、本文は空です。作成したユーザーのIDはLocationヘッダの末尾に入ります。26.8.1のUsersResourceは、作成後にResponse.createdでユーザーIDつきのURLを返しています。

usernameとexact=trueで1件に絞る検索と既定100件のページング

IDが分からないときは検索で引きます。usernameは既定で部分一致なので、exact=trueを付けないとtaroでtaro.yamadaもtaro2も返ります。

USER_ID=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
  "${KC}/admin/realms/${REALM}/users?username=taro.yamada&exact=true" | jq -r '.[0].id')

curl -s -H "Authorization: Bearer ${TOKEN}" "${KC}/admin/realms/${REALM}/users/count"

curl -s -H "Authorization: Bearer ${TOKEN}" \
  "${KC}/admin/realms/${REALM}/users?first=100&max=100&briefRepresentation=true"

一覧のmaxは省略すると100件です。全件を同期するなら、countで総数を取り、firstを100ずつ進めて回します。briefRepresentation=trueにすると属性を省いた軽い応答になり、件数の多いレルムで転送量を抑えられます。

レルムロールの割り当てとSMTP設定を使うパスワード再設定メールの送信

ロールの割り当ては、ロールの表現をそのまま配列に入れてrole-mappings/realmへPOSTします。メール送信のexecute-actions-emailは、レルムにSMTPの設定が無いと送れません。

ROLE=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
  "${KC}/admin/realms/${REALM}/roles/app-user")

curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "${KC}/admin/realms/${REALM}/users/${USER_ID}/role-mappings/realm" \
  -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
  -d "[${ROLE}]"

curl -s -o /dev/null -w "%{http_code}\n" -X PUT \
  "${KC}/admin/realms/${REALM}/users/${USER_ID}/execute-actions-email?lifespan=86400" \
  -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
  -d '["UPDATE_PASSWORD","VERIFY_EMAIL"]'

どちらも成功時は204です。lifespanはメール内リンクの有効秒数で、例では24時間にしています。初期パスワードを渡さずにメールで本人に設定させる運用なら、作成時のcredentialsを省いてこのメールだけを送る形にできます。

退職者の無効化はGETした表現を書き換えてPUTしセッションも切る

退職者は削除より無効化が安全です。監査ログと紐付いたユーザーIDが残るからです。PUT /users/{id}には、一度GETした表現のenabledだけをfalseに変えて送り返します。

curl -s -H "Authorization: Bearer ${TOKEN}" "${KC}/admin/realms/${REALM}/users/${USER_ID}" \
  | jq '.enabled = false' > user.json

curl -s -o /dev/null -w "%{http_code}\n" -X PUT \
  "${KC}/admin/realms/${REALM}/users/${USER_ID}" \
  -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d @user.json

curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "${KC}/admin/realms/${REALM}/users/${USER_ID}/logout" \
  -H "Authorization: Bearer ${TOKEN}"

GETした表現を戻す形にしておけば、ほかの属性を意図せず書き換える心配がありません。最後のlogoutは、そのユーザーの全セッションを消します。ただし発行済みのアクセストークンを署名の検証だけで受け入れるAPIでは、そのトークンは期限まで通ります。寿命を短く保つのはこのためです。

Node.jsから呼ぶ場合:keycloak-admin-clientで同じ処理を書く方法

アプリの中で呼ぶなら、curlを組み立てるより公式のクライアントライブラリが楽です。npmの@keycloak/keycloak-admin-clientはサーバーと同じ26.8.1が最新です。

client_credentialsで認証し58秒ごとに取り直す公式READMEの書き方

公式READMEは、リフレッシュトークンが無いclient_credentialsではauthを58秒ごとに呼び直す例を載せています。同じ考え方で、ユーザー作成とロール付与を書くと次のようになります。

import KcAdminClient from '@keycloak/keycloak-admin-client';

const kc = new KcAdminClient({ baseUrl: 'http://localhost:8080', realmName: 'myapp' });
const credentials = {
  grantType: 'client_credentials',
  clientId: 'user-sync',
  clientSecret: process.env.KC_CLIENT_SECRET,
};
await kc.auth(credentials);
setInterval(() => kc.auth(credentials), 58 * 1000);

const { id } = await kc.users.create({
  username: 'taro.yamada',
  email: process.env.NEW_USER_EMAIL,
  enabled: true,
});
const role = await kc.roles.findOneByName({ name: 'app-user' });
await kc.users.addRealmRoleMappings({ id, roles: [{ id: role.id, name: role.name }] });

呼んでいるのは前章と同じエンドポイントなので、必要なロールも変わりません。トークンの寿命を5分以上に延ばしているレルムでも、58秒の間隔はそのままで害はありません。

Keycloak APIで詰まりやすいエラーと応答コードごとの切り分け

Admin REST APIのエラーは、本文が短いか空のことが多く、応答コードから原因を絞るのが早道です。

401・403・404・409の4つで原因を切り分ける確認順

コード よくある原因 確認すること
401 トークン切れ・別レルムのトークン expires_inとトークンのissのレルム名
403 管理ロール不足 トークン内の管理ロール
404 clientIdと内部IDの取り違え パスに入れたのがUUIDか
409 同じusernameかemailのユーザーが既存 exact=trueで検索して既存IDを使う

表の403では、トークンのresource_accessにmanage-usersなどの管理ロールがあるかを確認してください。403で最初に疑うのは、サービスアカウントにロールを付けたのにスコープ側へ割り当てていないケースです。トークンをデコードしてrealm-managementのロールが空なら、前述の手順4が抜けています。409は作成処理を再実行したときに出やすいので、同期バッチは「検索して無ければ作成」の順に組みます。

Admin REST APIで連携を自作する判断:採用する条件と見送る場面

APIで何でも書けるぶん、作らなくてよい連携まで自作しがちです。採用と見送りの線を条件で示します。

会員DBや人事データとの同期をAPIで組むべき条件とSCIM・LDAPとの使い分け

APIでの自作が合うのは、自社サービスの会員登録画面からKeycloakにユーザーを作る場合と、正本がCSVや独自DBで標準プロトコルを話せない場合です。入社・異動・退職を夜間バッチで反映する程度なら、本記事の4つのAPIで足ります。

見送るべきなのは、正本がLDAPやActive Directoryの場合です。User Federationで読み込めば同期のコードは要りません。SaaS側がSCIMを話すなら、SCIMでのプロビジョニングに寄せたほうが保守の手間が減ります。26.8系の管理ガイドにはSCIM APIがAdmin REST APIと同じ権限モデルで動くとの記載もあります。

masterレルムの管理者資格情報をアプリに持たせる構成を避ける理由

公式ガイドの最初の例どおり、masterのadminでトークンを取るコードを本番に残す構成は採用しません。そのトークンは全レルムの全設定を変えられ、漏えい時の被害がKeycloak全体に及ぶからです。管理ガイドもmasterはレルムの作成と管理だけに使うよう書いています。

レルムごとにサービスアカウントを分け、ロールをmanage-usersとview-realmに絞れば、漏れても影響はそのレルムのユーザー管理に留まります。Client Secretは定期的に再生成し、Keycloak自体も修正版へ更新する手順と合わせて運用してください。権限設計からID連携の実装までまとめて任せたい場合は、一創の認証基盤・ID管理システム開発で、既存システムとの同期方式の選定から相談を受けています。

よくある質問

Keycloak APIを使い始める開発者からよく出る質問に、26.8.1時点の仕様で答えます。

Keycloak APIのドキュメントはどこにありますか?

エンドポイントの一覧はkeycloak.orgの「Keycloak Admin REST API」リファレンスにあり、同じ場所のopenapi.jsonで機械的に読めます。トークンの取り方はServer Developer Guide、管理ロールの意味はServer Administration Guideという分担です。版ごとにURLのlatestを26.8.1のような版番号に替えると、その版の内容に固定できます。

admin-cliで取ったトークンがすぐ使えなくなるのはなぜですか?

masterレルムのトークン寿命が短く設定されているためで、公式ガイドも既定で1分と書いています。スクリプトの途中で401が出たら、トークンを取り直してください。寿命を延ばすより、本記事のようにレルムごとのサービスアカウントへ切り替えるほうが、権限も絞れて安全です。

Keycloak APIでユーザーのパスワードを直接設定できますか?

ユーザーのパスワードを直接設定することは可能です。PUT /admin/realms/{realm}/users/{id}/reset-passwordにtype・value・temporaryを持つJSONを送ると、204で設定されます。レルムのパスワードポリシーに合わない値を送った場合の応答は400です。本人に決めさせたい場合は、execute-actions-emailでUPDATE_PASSWORDを送る方法を選びます。

グループへの追加もAPIでできますか?

できます。PUT /admin/realms/{realm}/users/{id}/groups/{groupId}で、本文なしの呼び出しで所属させられます。groupIdはグループ名ではなく内部IDです。ロールをグループに持たせて所属で権限を配る設計は、Keycloakの権限モデルで扱っています。

Docker上のKeycloakでも同じURLで呼べますか?

呼べます。パスは同じで、先頭のホスト名とポートが変わるだけです。リバースプロキシの後ろに置く場合は、/admin/を外部に公開しない構成が多いため、同期バッチは内部ネットワークから呼びます。公開パスの絞り方はKeycloak Dockerの本番構成で解説しています。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.05 テックブログ Linuxカーネルの脆弱性CVE-2026-98432:修正版の見分け方とnftablesの緩和策
  2. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  3. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号
  4. 2026.10.31 テックブログ 松屋の不正アクセス:237万件の対象確認と窓口、漏れたパスワードの守り方
  5. 2026.11.04 テックブログ CVE-2026-119977:WordPress更新中のRCEと安全な更新手順

RELATED POSTS 関連記事

目次