KeycloakのSAML設定は、アプリに対してIdPになる「SAMLクライアント」と、外部のIdPに対してSPになる「Identity Providers」の2画面に分かれます。本記事の対象は後者、社員がMicrosoft Entra IDのアカウントでKeycloak配下のアプリにログインするブローカー構成です。2026年11月5日時点の最新版Keycloak 26.8.1を前提に、Entra ID側の登録値、kcadm.shでの登録コード、NameIDの形式、属性マッパー、初回ログインフロー、証明書の更新を順に解説します。
まとめ:Keycloak SAMLのブローカー構成はNameIDと初回ログインを先に決める
Entra IDを外部IdPにするなら、Keycloak側はNameIDの形式を既定のpersistentのまま使い、Principal typeもSubject NameIDのままにします。Entra IDはpersistentを要求されるとアプリごとに固定のペアワイズ識別子を返すため、メールアドレスが変わっても紐付けが切れません。メールと氏名はAttribute Importerで、ユーザー名はUsername Template ImporterでUPNから作ります。
設定値の多くは画面で入れるより、kcadm.shとメタデータから組むほうが再現できます。初回ログインで既存アカウントと自動でつなぐ設定は、利用者が自分で登録できないレルムに限ります。アプリが1つでIdPもEntra IDだけなら、Keycloakを挟まずEntra IDに直接つなぐほうが単純です。
KeycloakのSAMLがIdPとSPのどちら向きかで変わる設定画面と流れ
「Keycloak SAML」で調べると、2つの向きの手順が混ざって出てきます。最初に決めておくべきなのは、自分の作業がどちらの向きに当たるかです。Keycloakの全体像はKeycloakとは?Oktaとの違いと連携手順で整理しています。
SAMLクライアントとIdentity Providersの2画面を使い分ける基準
| Keycloakの役割 | 設定する画面 | SAMLの相手 |
|---|---|---|
| IdP(アサーションを出す) | Clients | 業務アプリ・SaaS |
| SP(アサーションを受ける) | Identity providers | Entra ID・AD FS |
SaaSの管理画面に「IdPのメタデータURL」を入れる作業なら前者で、Keycloakは自分のレルムのメタデータを渡す側です。この手順はSAML 2.0 IdPの構築手順で、SP登録・Name ID・署名・マッパーまで扱いました。社内のEntra IDのアカウントでKeycloakにログインさせたいなら後者です。本記事は後者に絞ります。
アプリ・Keycloak・Entra IDの間を流れる2本のSAMLとIssuerの一致
ブローカー構成では、やり取りが2本あります。アプリからKeycloakへの1本目は、OIDCでもSAMLでも構いません。Keycloakが社員をEntra IDのSAMLプロトコルに送る2本目が本記事の対象で、KeycloakはAuthnRequestを送ってアサーションを受けるSPとして動きます。
Keycloakが名乗るIssuerは、Entra IDに登録した識別子と完全に一致する必要があります。既定の識別子はレルムのURLなので、KC_HOSTNAMEを後から変えるとEntra IDが要求を受け付けなくなる原因になります。識別子の一致を保つには、公開URLの事前確定が必要です。公開URLの固め方はKeycloak Dockerの本番構成で解説しています。
Entra IDのエンタープライズアプリにKeycloakをSPとして登録する手順
先にEntra ID側で、Keycloakを1つのSAMLアプリとして登録します。以下、Keycloakの公開URLをhttps://auth.example.com、レルムをcorp、IdPのエイリアスをentra-idとします。
識別子と応答URLに入れるKeycloakのbroker endpointの形
Entra管理センターのエンタープライズアプリから、ギャラリー外のアプリを作成します。SAML SSOの有効化手順のとおり、クラウドアプリケーション管理者以上の権限が必要です。「基本的なSAML構成」には次の値を入れます。
識別子(エンティティID) : https://auth.example.com/realms/corp
応答URL(ACS URL) : https://auth.example.com/realms/corp/broker/entra-id/endpoint
サインオンURL・リレー状態 : 空欄
応答URLのentra-idは、Keycloakで付けるエイリアスと一致させます。KeycloakのServer Administration Guideによると、識別子はService Provider Entity IDの既定値で、<root>/realms/{realm-name}の形です。保存後、「SAML証明書」の欄にあるアプリのフェデレーションメタデータURLを控え、社員とグループのアプリへの割り当ても済ませます。
NameIDの形式でEntra IDが返す値が変わる仕組みとpersistentの選択
Entra IDの画面でNameIDの値をメールアドレスに変えても、Keycloakに届くとは限りません。クレームのカスタマイズの説明は、要求にNameIDPolicyの形式が入っていればその形式に従うと書いています。Keycloakは既定でpersistentを要求するので、Entra IDの設定より要求の形式が先に効きます。
| Keycloakが要求する形式 | Entra IDが返すNameID |
|---|---|
| persistent(Keycloakの既定) | ペアワイズ識別子 |
| emailAddress | メールアドレスの形 |
| unspecified | ペアワイズ識別子 |
| transient | 毎回変わる乱数 |
表はSAMLプロトコルのページにある対応です。ペアワイズ識別子はKeycloak専用の値で、他人に再割り当てされません。メールアドレスが変わっても紐付けが残るため、本記事はpersistentを選びます。transientは、KeycloakもSubject NameIDとの組み合わせを禁じています。形式の不一致で失敗したときの見分け方はSAML NameIDFormatとメタデータの不一致でSSOが失敗する原因の切り分けにまとめました。
グループクレームを150件の上限とオブジェクトIDの前提で出す設定
ロールの付与にEntra IDのグループを使うなら、「グループ要求の追加」でグループクレームを出します。グループクレームの説明によると、SAMLのアサーションに載るグループは入れ子を含めて150件までです。超えるとグループクレーム自体が出なくなります。
値は既定でグループのオブジェクトIDです。大きな組織では「アプリケーションに割り当てられたグループ」を選び、ロールに対応するグループだけを出します。この選択では入れ子のグループが含まれないため、社員をグループの直接のメンバーにします。
kcadm.shでEntra IDをSAML v2.0の外部IdPに登録するコード
管理コンソールでもメタデータURLから取り込めますが、本番と検証に同じ値を入れるなら、同梱のAdmin CLIkcadm.shで組むほうが確実です。
メタデータから識別子・SSOのURL・署名証明書を取り出すPython
Entra IDのメタデータから、Keycloakに渡す4つの値を取り出します。標準ライブラリだけで動き、出力はシェルの変数定義の形です。
# entra_meta.py:Entra ID のフェデレーションメタデータから kcadm.sh に渡す値を取り出す
import sys, urllib.request, xml.etree.ElementTree as ET
NS = {"md": "urn:oasis:names:tc:SAML:2.0:metadata",
"ds": "http://www.w3.org/2000/09/xmldsig#"}
REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
root = ET.fromstring(urllib.request.urlopen(sys.argv[1], timeout=20).read())
idp = root.find("md:IDPSSODescriptor", NS)
sso = idp.find(f"md:SingleSignOnService[@Binding='{REDIRECT}']", NS)
slo = idp.find(f"md:SingleLogoutService[@Binding='{REDIRECT}']", NS)
certs = [c.text.strip() for c in idp.findall(
"md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate", NS)]
print(f'IDP_ENTITY_ID="{root.get("entityID")}"')
print(f'SSO_URL="{sso.get("Location")}"')
print(f'SLO_URL="{slo.get("Location") if slo is not None else ""}"')
print(f'SIGNING_CERT="{",".join(certs)}"')
Python 3.14でEntra IDの公開メタデータを読ませると、IDP_ENTITY_IDにhttps://sts.windows.net/で始まる発行者、SSO_URLに/saml2で終わるURLが出ました。発行者の形は、SAMLプロトコルのページにあるhttps://sts.windows.net/<テナントID>/と一致します。証明書が複数あるときはカンマでつなぎます。Keycloakはカンマ区切りで複数の検証用証明書を受け付けるためです。メタデータの各要素の読み方はSAMLメタデータとはで解説しています。
kcadm.shでIdentity Providerを作る設定例と各キーの意味
取り出した値で、SAML v2.0のIdentity Providerを作ります。キー名は26.8.1のSAMLIdentityProviderConfig.javaで確かめたものです。
# Keycloak 26.8.1 同梱の kcadm.sh で Entra ID を SAML v2.0 の IdP として登録する
eval "$(python3 entra_meta.py "$APP_METADATA_URL")"
kcadm.sh config credentials --server https://auth.example.com --realm master --user admin
kcadm.sh create identity-provider/instances -r corp \
-s alias=entra-id -s providerId=saml -s enabled=true \
-s 'displayName=社員アカウント(Entra ID)' \
-s config.idpEntityId="$IDP_ENTITY_ID" \
-s config.singleSignOnServiceUrl="$SSO_URL" \
-s config.singleLogoutServiceUrl="$SLO_URL" \
-s config.nameIDPolicyFormat=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent \
-s config.principalType=SUBJECT \
-s config.postBindingResponse=true \
-s config.validateSignature=true \
-s config.signingCertificate="$SIGNING_CERT" \
-s config.allowedClockSkew=30 \
-s config.syncMode=FORCE
APP_METADATA_URLには前の章で控えたメタデータURLを入れます。idpEntityIdを入れると受け取ったアサーションの発行者が照合され、空なら照合されません。syncMode=FORCEは、ログインのたびにEntra IDの値でユーザー情報を上書きする指定です。
署名検証・時刻のずれ・Pass subjectで起きるログイン失敗
登録できてもログインで失敗するとき、原因は次の3つに集中します。
- 時刻のずれ:Entra IDの
NotBeforeは発行時刻と同じで余裕を持たせない。KeycloakのallowedClockSkewは既定0秒なので、時計が少し遅れるだけで弾かれる - 署名検証:
validateSignatureを有効にして証明書を入れないと検証できない。Entra IDで証明書を切り替えた直後にも起きる - Pass subject:有効にすると要求に
Subjectが入るが、Entra IDはSubject付きの要求をエラーにする
上の例のallowedClockSkew=30は1つ目への対策で、サーバーのNTP同期も確かめます。どこで止まるか分からないときは、SAML-tracerの使い方の手順で通信を採取し、要求と応答のどちらが原因かを切り分けます。
Attribute ImporterでEntra IDのクレームを取り込む属性マッパー設定
IdPを登録しただけでは、Keycloakのユーザーにメールや氏名は入りません。Identity Providerの「Mappers」で、アサーションの属性をKeycloakのユーザー属性へ写します。
Entra IDのメール・氏名・ユーザー名を写すマッパー3本のJSON定義
Entra IDは既定で、ユーザー名(UPN)・メールアドレス・名・姓のクレームを出します。SAML用のマッパーのIDは、UserAttributeMapper.javaなどのソースにあるsaml-user-attribute-idp-mapper(Attribute Importer)とsaml-username-idp-mapper(Username Template Importer)です。
# 属性マッパーを3本作る(Sync mode は IdP の設定を継承)
C='http://schemas.xmlsoap.org/ws/2005/05/identity/claims'
for pair in email:emailaddress firstName:givenname lastName:surname; do
kcadm.sh create identity-provider/instances/entra-id/mappers -r corp -f - <<EOF
{"name": "${pair%%:*}", "identityProviderAlias": "entra-id",
"identityProviderMapper": "saml-user-attribute-idp-mapper",
"config": {"syncMode": "INHERIT", "attribute.name": "$C/${pair#*:}",
"user.attribute": "${pair%%:*}"}}
EOF
done
# ユーザー名は UPN を小文字にして作る(${...} を展開させないため 'EOF' で囲む)
kcadm.sh create identity-provider/instances/entra-id/mappers -r corp -f - <<'EOF'
{"name": "username-from-upn", "identityProviderAlias": "entra-id",
"identityProviderMapper": "saml-username-idp-mapper",
"config": {"syncMode": "INHERIT", "target": "LOCAL",
"template": "${ATTRIBUTE.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name | lowercase}"}}
EOF
Username Template Importerを入れないと、ユーザー名がペアワイズ識別子の文字列になり、管理画面で誰なのか分かりません。${ATTRIBUTE.名前}と| lowercaseはソースの説明文にある書き方で、targetのLOCALはKeycloak側のユーザー名に入れる指定です。
SAML Attribute to RoleでEntra IDのグループのロール変換設定
グループクレームをロールに変えるには、saml-role-idp-mapper(SAML Attribute to Role)を使います。属性名と値が一致したユーザーに、指定したロールを付けます。
# 営業部グループの社員にレルムロール sales を付ける
kcadm.sh create identity-provider/instances/entra-id/mappers -r corp -f - <<'EOF'
{"name": "group-sales", "identityProviderAlias": "entra-id",
"identityProviderMapper": "saml-role-idp-mapper",
"config": {"syncMode": "FORCE",
"attribute.name": "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups",
"attribute.value": "<営業部グループのオブジェクトID>", "role": "sales"}}
EOF
ロールのマッパーだけsyncModeをFORCEにしているのは、異動でグループから外れた社員のロールを次のログインで外すためです。管理ガイドの説明では、importは初回ログインの時点でしか値を取り込まず、初回に付いたロールが残り続けます。複数の値や正規表現で判定したいときは、Advanced Attribute to Roleを使います。
初回ログインフローで既存アカウントとの紐付けと自動作成を制御する方法
Entra IDの社員が初めてKeycloakに来たとき、ユーザーを作るのか、既存のユーザーにつなぐのかは、Identity Providerの「First login flow」で決まります。既定はfirst broker loginです。
first broker loginの既定の動きとメールアドレス一致時の確認
管理ガイドのFirst login flowの節によると、既定のフローは次の順で動きます。
- Review Profile:必須項目が欠けたときに属性の確認画面を出す
- Create User If Unique:同じメールかユーザー名が無ければ作成して終了
- Confirm Link Existing Account:同じメールがあれば、つなぐか確認する
- Verify Existing Account By Email/By Re-authentication:メールか既存のパスワードで本人確認する
Entra IDのメールは組織が管理しているので、Identity Providerの「Trust Email」をONにします。こうするとKeycloakはメールを確認済みとして扱い、利用者にメールの確認を求めません。
既存アカウントへの自動リンクを使ってよい条件とユーザー作成を止める設定
確認画面を出さずに既存アカウントへつなぐAutomatically Set Existing Userという部品もあります。管理ガイドは、利用者が任意のメールアドレスで登録できる環境では危険だと明記しています。他人のメールで先に登録すれば、その人のログインを乗っ取れるためです。使ってよいのは、自己登録を無効にし、アカウントを管理者だけが作るレルムに限ります。
反対に、社員のアカウントを先にLDAPなどで作っておき、それ以外の人を入れたくない場合もあります。このときはfirst broker loginのCreate User If UniqueとConfirm Link Existing Accountを無効にします。Keycloakに無い人は、Entra IDで認証できてもログインできません。
kc_idp_hintでログイン画面を飛ばし証明書の3年更新に備える運用
Hide on Login Pageとkc_idp_hintによる社員の直接転送設定
社員が全員Entra IDを使うなら、アプリからKeycloakへ送る認可リクエストのURLにkc_idp_hint=entra-idを付けます。この指定による転送先は、Keycloakのログイン画面を経由しないEntra IDのサインインです。IdPの「Hide on Login Page」をONにすると、画面のボタンは消えてもヒント付きのURLからは使えます。
ログアウト時は、Keycloakがログインに使った外部IdPへも要求を送ります。送り先はsingleLogoutServiceUrlで、前のコードではメタデータの値を入れています。
署名証明書の既定3年の期限とメタデータURL参照による証明書の取り込み
証明書の管理手順によると、Entra IDがSAMLアプリ用に自動で作る署名証明書の期限は3年です。期限の60日前・30日前・7日前に通知メールが届きます。同じページは、アプリ側がメタデータURLを少なくとも24時間ごとに確認し、新しい証明書を取り込む運用を勧めています。
Keycloakでこれに近い動きにする設定は、Identity Providerの「Use metadata descriptor URL」をONにする方法です。管理ガイドによれば、証明書はメタデータURLから取得して既定24時間キャッシュされ、POSTの応答に知らない証明書が来ればその場で取り直します。OFFのままならsigningCertificateを手で入れ替えるので、通知メールの宛先に運用担当者を加えます。
Keycloak SAMLのブローカー構成を採用する条件とEntra IDへ直接つなぐ場面
ここまでの設定は、アプリとEntra IDの間にKeycloakを1段挟む構成です。挟む理由が無ければ、段は少ないほど壊れません。
複数アプリに同じロールを配るならKeycloakを挟む判断基準
Keycloakを挟むのは、OIDCとSAMLのアプリが混在していてロールの付け方を1か所で決めたい場合です。Entra IDの社員と取引先のIdPの利用者を同じアプリに入れる場合や、自社のSaaSが顧客ごとに異なるIdPを受ける場合も同じ判断になります。
挟むと、Entra IDからは配下の全アプリが1つのエンタープライズアプリに見えます。アプリごとの割り当てや条件付きアクセスはEntra ID側で分けられず、Keycloakのロールとクライアントの設定で持つことになります。同じ方式をAWSで組む場合はCognito SAML連携の手順と比べてください。
社内IdPが1つでアプリも少ない場合にKeycloakの導入を見送る条件
社員だけが使う業務アプリが1つか2つで、IdPもEntra IDだけなら、Keycloakは挟みません。アプリをEntra IDに直接SAMLかOIDCで登録すれば、条件付きアクセスもアプリ単位で効きます。Keycloakの運用、つまり2週間前後で出る修正版への追従とDBのバックアップを抱える理由がありません。修正版の頻度はKeycloakの脆弱性対応の手順で、26.8.1のリリースなどの実例とともに整理しました。
AD FSからの移行の途中で、IdPそのものの置き場を決めかねている場合は、先にSAML ADの構成手順でAD FSとEntra IDの役割を決めます。Keycloakを挟むかどうかの判断から、ブローカー構成とマッパーの設計、既存システムの移行までを任せたい場合は、一創の認証基盤・ID管理システム開発で支援しています。
Keycloak SAMLの設定で導入担当者からよく受ける質問と回答
KeycloakでSAMLを設定するときに出やすい疑問をまとめました。
KeycloakをSAMLのIdPにしてSaaSへSSOする手順はどこを見ればよいですか?
KeycloakがIdPになる向きは、管理コンソールの「Clients」でSAMLクライアントを作る作業です。SaaSのエンティティIDをClient IDに入れたうえで、Name IDの形式と署名をSaaSの要求に合わせる設定が必要です。KeycloakのIdPメタデータは/realms/corp/protocol/saml/descriptorで公開されます。詳しい手順はSAML 2.0 IdPの構築手順で扱っています。
Entra IDとKeycloakはSAMLとOIDCのどちらでつなぐべきですか?
KeycloakはOIDCのIdentity Providerにも対応しているので、どちらでもつなげます。新規に組むならOIDCが簡単で、鍵は公開鍵のエンドポイントから自動で取得され、証明書の手作業での入れ替えがありません。SAMLを選ぶのは、社内の規程でSAMLを指定されている場合や、AD FSなどSAMLしか話せないIdPと設計をそろえたい場合です。
既存のKeycloakユーザーとEntra IDのアカウントを事前につなげますか?
Admin REST APIでユーザーに外部IdPとの紐付け(federated identity)を追加する方法はあります。ただし紐付けに使うuserIdは、Entra IDが返すペアワイズ識別子そのもので、事前には分かりません。実際には初回ログインのConfirm Link Existing Accountでつなぐか、自己登録を止めたうえで自動リンクを使う方法が現実的です。
AD FSを外部IdPにするときはEntra IDと何が違いますか?
Keycloak側の手順は同じで、メタデータの取り込みとマッパーの作成という流れです。違いの1つは署名の鍵の名前で、管理ガイドはAD FSがKeyNameに証明書のサブジェクトを期待すると書いているため、SAML Signature Key NameをCERT_SUBJECTにします。もう1つはクレームで、AD FSでは発行変換規則を自分で書きます。AD FS側の設定はSAML ADの構成手順を参照してください。
Entra IDのグループが150件を超える社員はどうなりますか?
その社員のアサーションにはグループクレームが載らず、ロールのマッパーは何も付けません。ログイン自体は成功するので、気づきにくい失敗です。対策は、グループクレームを「アプリケーションに割り当てられたグループ」に絞り、ロールに対応するグループだけをKeycloakのアプリに割り当てることです。入れ子のグループは展開されません。
関連記事
- SAML 2.0 IdPの構築手順:KeycloakとIDaaSの選び方とSP登録・属性マッピング:KeycloakをIdPにしてアプリへSSOする向きの手順です。
- Keycloakとは?Oktaとの違いと連携手順|Auth0・Cognito比較で導入を判断【2026年10月】:Keycloakの機能と他製品との違いを確認できます。
- SAMLとは?認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説:IdPとSPの役割とSAMLの流れの基礎です。
- SAML NameIDFormatとメタデータの不一致でSSOが失敗する原因の切り分け:NameIDの形式が合わないときの原因の探し方です。
- Keycloak Dockerの本番構成:Composeでの起動とリバースプロキシ・ヘルスチェックの設定:識別子の元になる公開URLとプロキシの設定です。