セキュリティ

Keycloak SSOの設定手順:複数アプリのOIDCクライアント登録とセッション・ログアウト連携

AIを活用したセキュリティソリューションのサービス内容

KeycloakでSSOを組む作業は、同じレルムにアプリをOIDCクライアントとして並べるところから始まります。1つ目のアプリでログインすると、2つ目のアプリではログイン画面が出ずに戻ってくる状態がゴールです。本記事では2026年11月6日時点の最新版Keycloak 26.8.2を前提に、kcadm.shでのクライアント登録、SSOセッションの時間設定、シングルログアウトの受け口、外部IdPを足すIdentity Brokeringの組み方を、コードとともに解説します。

まとめ:Keycloak SSOはアプリごとのクライアント登録とログアウト経路で決まる

SSOに参加させるアプリは、同じレルムに1アプリ1クライアントで登録します。サーバーを持つWebアプリはconfidentialクライアントにし、PKCEを必須にして、Direct Access Grantsは無効にする設定です。REST APIやkcadm.shで作ると、指定しない項目はpublic clientとDirect Access Grants有効で作られるため、明示的に打ち消します。

時間の既定値は、SSOセッションが無操作30分・最長10時間、アクセストークンが5分です。ログアウトはバックチャネルで各アプリに届け、アプリ側でLogout Tokenを検証してセッションを消します。社員がEntra IDやOktaでログインする場合も、SSOのセッションを持つのはKeycloakです。アプリが2〜3個でIdPが1つなら、Keycloakを置かずIDaaSに直接登録するほうが運用は軽く済みます。

KeycloakのSSOが1回のログインで複数アプリに効く仕組みとレルムの境界

設定に入る前に、Keycloakが何を共有してSSOを成り立たせているかを押さえます。SSOの方式そのものの違いはシングルサインオン(SSO)とはで整理しています。

SSOセッション1つにアプリ別のクライアントセッションがぶら下がる親子関係

Server Administration GuideのSession and token timeoutsは、SSOのユーザーセッションを0個以上のクライアントセッションの親と説明しています。クライアントセッションは、利用者がログインしたアプリごとに1つずつ作られます。

流れはこうです。アプリAでログインするとKeycloakのドメインにセッションのCookieが残ります。続いてアプリBが認可リクエストを送ると、KeycloakはCookieからSSOセッションを見つけ、ログイン画面を出さずに認可コードを返します。アプリBのクライアントセッションが親の下に追加されるのは、このタイミングです。アプリ側の仕組みはOIDCの認可コードフローそのもので、IDトークンの検証まで含めた基礎はOIDC(OpenID Connect)とはで解説しました。

SSOが効く範囲は同じレルムの中だけという境界とレルムを分ける判断

SSOのセッションはレルムの中に閉じています。社員向けと顧客向けでレルムを分ければ、その2つの間ではSSOしません。これは欠点ではなく、利用者の母集団・パスワードポリシー・管理者の権限を分けるための境界です。

判断の基準は単純です。同じ人が行き来するアプリは同じレルムに入れ、別の母集団が使うアプリはレルムを分けます。レルムをアプリの数だけ作る構成は、SSOが成立しなくなるうえに管理の手間も増えるため避けます。

kcadm.shで2つのアプリをOIDCクライアントとして登録するSSOの設定コード

以下、Keycloakの公開URLをhttps://auth.example.com、レルムをcorp、アプリをapp-aとapp-bとします。公開URLの固め方とリバースプロキシの設定はKeycloak Dockerの本番構成を参照してください。

kcadm.shでconfidentialクライアント2つと転送先URIを設定

管理コンソールでも作れますが、検証環境と本番に同じ値を入れるなら同梱のAdmin CLIで組むほうが再現できます。属性のキー名は26.8.2のOIDCLoginProtocolFactory.javaなどのソースで確かめたものです。

# Keycloak 26.8 系の kcadm.sh で、SSO に参加する 2 つのアプリを confidential クライアントとして登録する
kcadm.sh config credentials --server https://auth.example.com --realm master --user admin

for APP in app-a app-b; do
  kcadm.sh create clients -r corp \
    -s clientId=$APP -s protocol=openid-connect -s enabled=true \
    -s publicClient=false -s standardFlowEnabled=true \
    -s directAccessGrantsEnabled=false -s implicitFlowEnabled=false \
    -s frontchannelLogout=false \
    -s "redirectUris=[\"https://$APP.example.com/oidc/callback\"]" \
    -s "webOrigins=[\"https://$APP.example.com\"]" \
    -s 'attributes."pkce.code.challenge.method"=S256' \
    -s "attributes.\"post.logout.redirect.uris\"=https://$APP.example.com/" \
    -s "attributes.\"backchannel.logout.url\"=https://$APP.example.com/backchannel-logout" \
    -s 'attributes."backchannel.logout.session.required"=true'
done

# アプリに渡すクライアントシークレットを取り出す
ID=$(kcadm.sh get clients -r corp -q clientId=app-a --fields id --format csv --noquotes)
kcadm.sh get clients/$ID/client-secret -r corp

redirectUrisにはワイルドカードを使わず、コールバックのURLを1本ずつ書きます。*で広く許すと、認可コードを攻撃者のURLへ渡す余地が残るためです。アプリ側には、Client ID・シークレット・/realms/corp/.well-known/openid-configurationのURLの3つを渡せば足ります。Securing applications with OpenID Connectによると、多くのライブラリはこのディスカバリ文書から各エンドポイントを読み取ります。

RESTでのクライアント作成時に確認する公開設定とパスワード認証の既定値

前のコードでpublicClient=falseやdirectAccessGrantsEnabled=falseをわざわざ書いたのには理由があります。OIDCLoginProtocolFactory.javaのsetupClientDefaultsは、指定の無い項目を次の値で埋めます。表の最後の行は属性backchannel.logout.session.requiredです。

項目 指定しないときの既定 SSOのWebアプリでの推奨
publicClient true(シークレット無し) false
standardFlowEnabled true true
directAccessGrantsEnabled true false
implicitFlowEnabled false false
frontchannelLogout false false
ログアウト通知へのsid同梱 true true

Direct Access Grantsは、アプリがIDとパスワードを直接受け取ってトークンに換える方式です。有効のままだとKeycloakのログイン画面を通らないため、SSOも多要素認証も効かない入口が1つ残ります。Terraformや自作のスクリプトで大量にクライアントを作る場合も同じ既定が効くので、テンプレートに2項目を入れておきます。Admin REST APIを直接呼ぶ書き方はKeycloak APIの使い方にまとめました。

2つ目のアプリでログイン画面が出ないことを確かめるSSOの動作確認

SSOが効いているかは、ブラウザの操作とKeycloak側のセッションの両方で確かめます。

  1. シークレットウィンドウでアプリAを開き、Keycloakの画面でログインする
  2. 同じウィンドウでアプリBを開き、ログイン画面が出ずにアプリBへ戻ることを見る
  3. 管理コンソールのSessions、またはkcadm.sh get users/$USER_ID/sessions -r corpで、1つのセッションのclientsにapp-aとapp-bの両方が載っているかを見る

手順2でログイン画面が出るなら、アプリBが別のレルムを向いているか、Keycloakの公開URLがアプリごとに違っています。Cookieはホスト名ごとに分かれるため、片方がauth.example.com、もう片方が内部のホスト名を使うとSSOしません。

SSO Session Idleの既定30分とMax 10時間を業務の使われ方で決める設定

SSOの体感を決めるのは、レルムのSessionsタブとTokensタブの値です。既定値は26.8.2のConstants.javaに定数で書かれています。

レルムのセッション3項目の既定値と業務システムでの決め方の目安

画面の設定名 kcadmのキー 既定値 意味
SSO Session Idle ssoSessionIdleTimeout 1800秒=30分 無操作でSSOセッションが切れるまで
SSO Session Max ssoSessionMaxLifespan 36000秒=10時間 操作があっても切れる上限
Access Token Lifespan accessTokenLifespan 300秒=5分 アクセストークンの有効期間

管理ガイドによると、Idleのタイマーはアプリが認証を求めたときやリフレッシュトークンを使ったときに戻ります。社員が昼休みを挟んでも再ログインさせたくないなら、Idleを1時間程度に延ばし、Maxは始業から終業までを覆う10時間のまま残します。変更に必要なのは次の1行です。

# 無操作 1 時間・最長 10 時間・アクセストークン 5 分にする
kcadm.sh update realms/corp \
  -s ssoSessionIdleTimeout=3600 -s ssoSessionMaxLifespan=36000 -s accessTokenLifespan=300

アクセストークンは短いまま残します。トークンを失効させる手段は限られており、漏れたときに使える時間はこの値で決まるためです。

アプリ単位の上書きはSSOセッションより短くするという管理ガイドの制約

経理や人事のように、同じレルムでも特定のアプリだけ早く切りたい場合は、クライアントのAdvancedタブでClient Session IdleとClient Session Maxを上書きします。管理ガイドは、この値をレルムのSSO Session Idle・SSO Session Maxより短くするよう書いています。クライアントセッションの期限は、親のSSOセッションを延ばしません。

反対向き、つまり1つのアプリだけ長く使わせたい要件はこの仕組みでは満たせません。その場合はアプリ側でリフレッシュトークンを使い続ける設計か、オフラインアクセスを検討します。

シングルログアウトをバックチャネルで受けるアプリ側の実装と設定

SSOはログインより、ログアウトで不具合が出やすい仕組みです。アプリAでログアウトしても、アプリBのセッションが残っていれば、共有端末では次の人がそのまま使えてしまいます。

iframe頼みのフロントチャネルがCSPとブラウザ終了で取りこぼす理由

管理ガイドのOIDC Logoutの節は、フロントチャネルのログアウトがログアウト画面の埋め込みiframeに頼るため、Content Security Policyで止められうると書いています。ログアウト要求が送られる前に利用者がブラウザを閉じれば、アプリ側のセッションは残ったままです。同じ節は、より確実な方法としてバックチャネルを勧めています。

注意点が1つあります。Backchannel logout URLは、クライアントのFront channel logoutがOFFのときだけ使われます。前のコードでfrontchannelLogout=falseを明示したのはこのためです。Front-Channel Logout 1.0は、サーバーを持たずブラウザのCookieだけでセッションを持つアプリに向く方式と考えます。

Logout Tokenを検証してsidでセッションを消すPythonの受け口

Keycloakは各アプリのBackchannel logout URLへ、logout_tokenを含むフォーム形式のPOSTを送ります。Back-Channel Logout 1.0が定める検証は、署名・iss・audの確認、eventsに専用のメンバーがあること、nonceが無いことです。成功なら200、不正なら400を返します。

# backchannel_logout.py:Keycloak の Logout Token を検証し、同じ sid のアプリ側セッションを消す
# 前提:pip install flask pyjwt[crypto](Flask 3 系・PyJWT 2 系)
import jwt
from flask import Flask, request

ISSUER = "https://auth.example.com/realms/corp"
CLIENT_ID = "app-a"
EVENT = "http://schemas.openid.net/event/backchannel-logout"
jwks = jwt.PyJWKClient(ISSUER + "/protocol/openid-connect/certs")
app = Flask(__name__)

# ログイン時に IDトークンの sid → アプリのセッションID を記録しておく(本番は Redis などに置く)
SESSIONS_BY_SID = {}

def delete_app_session(session_id):
    print("delete session", session_id)  # 実際はセッションストアから削除する

@app.post("/backchannel-logout")
def backchannel_logout():
    token = request.form.get("logout_token", "")
    try:
        key = jwks.get_signing_key_from_jwt(token).key
        claims = jwt.decode(token, key, algorithms=["RS256"],
                            audience=CLIENT_ID, issuer=ISSUER)
    except jwt.PyJWTError:
        return "", 400
    if EVENT not in claims.get("events", {}) or "nonce" in claims:
        return "", 400
    for session_id in SESSIONS_BY_SID.pop(claims.get("sid"), set()):
        delete_app_session(session_id)
    return "", 200, {"Cache-Control": "no-store"}

鍵はレルムのcertsエンドポイントから取るので、Keycloakで鍵を切り替えてもコードは変わりません。sidで消せるのは、クライアントのbackchannel.logout.session.requiredがtrueでLogout Tokenにsidが入るためです。受け口はKeycloakのサーバーから到達できる必要があるので、社内ネットワークのアプリならファイアウォールの許可も合わせて確かめます。

RP-Initiated LogoutのURLに渡すIDトークンとログアウト後の転送先

アプリのログアウトボタンは、自分のセッションを消したあとでKeycloakのログアウトエンドポイントへ利用者を送ります。ここでSSOセッションが消え、他のアプリへバックチャネルの通知が飛びます。

https://auth.example.com/realms/corp/protocol/openid-connect/logout
  ?id_token_hint=<ログイン時に受け取ったIDトークン>
  &post_logout_redirect_uri=https%3A%2F%2Fapp-a.example.com%2F

管理ガイドによると、post_logout_redirect_uriを付けるときはclient_idかid_token_hintのどちらかが必要です。値はクライアントのValid post logout redirect URIs、つまり前のコードのpost.logout.redirect.urisと一致しなければなりません。id_token_hintを付けないと、利用者にログアウトの確認画面が出ることがあります。パラメータの定義はRP-Initiated Logout 1.0にあります。

外部IdPを足してSSOを社員アカウントにつなぐIdentity Brokeringの構成

ここまでの構成では、利用者のパスワードをKeycloakが持ちます。社員がすでにEntra IDやOktaのアカウントを持っているなら、Keycloakに外部IdPを登録し、ログインをそちらへ任せます。

OIDCとSAMLの外部IdPの登録先とkc_idp_hintによるログイン画面の省略

外部IdPはレルムのIdentity providersに登録し、アプリ側の設定は一切変えません。登録の手順はIdPの種類で分かれるため、ここでは渡し先だけを示します。

社員全員が同じIdPを使うなら、アプリの認可リクエストにkc_idp_hint=<IdPのエイリアス>を付けます。管理ガイドによると、IdPの「Hide on Login Page」をONにしてもこのパラメータからは使えます。社員が進む先はKeycloakの画面ではなく、Entra IDやOktaのサインインです。

Keycloak側のログインで、ロールに応じてワンタイムパスワードを追加で求めるなど認証の強さを変えたい場合は、Keycloak 認証フローの設定手順で認証フローの複製と条件付きOTPの設定をまとめています。

外部IdP経由でもSSOセッションはKeycloakが持つという2段構成の注意

外部IdPでログインしても、アプリから見たSSOの主体はKeycloakのままです。アプリBへのSSOはKeycloakのセッションで成立し、外部IdPには問い合わせません。したがって前の章のIdleとMaxの値は、外部IdPを足したあとも効きます。

ログアウトについて管理ガイドは、Keycloakからログアウトするとき、最初のログインに使った外部IdPにも要求を送り、そこからもログアウトさせると説明しています。逆向き、つまりEntra IDで退職者のアカウントを止めた場合、すでに発行済みのKeycloakのセッションが残る期間は、最長でSSO Session Maxまでです。即時に止めたい場合は、Keycloak側でユーザーを無効にしてセッションを消す手順を退職の作業に入れます。

Keycloak SSOを自前で持つ条件とIDaaSへの直接登録で済ませる場面の判断

ここまでの設定は、Keycloakを自社で運用する前提です。SSOがほしいという理由だけで導入すると、運用の重さが効果を上回ることがあります。

自社開発のアプリが複数ありロールを1か所で決めたい場合の採用基準

Keycloakを持つのは、自社開発のアプリが複数あり、ロールの付け方を1か所で決めたい場合です。顧客向けの会員サイトと社内システムを同じ基盤で扱う場合や、取引先ごとに異なるIdPを受け入れる場合もKeycloakが向きます。IDaaSで利用者数に応じた月額が膨らむ規模なら、ライセンス費がかからない点も効いてきます。

一方で、修正版への追従は避けられません。26.8系だけでもGitHubの公開日(UTC)で26.8.0が10月1日、26.8.1が10月13日、26.8.2が11月5日と、約2週間おきに版が出ています。GitHubのリリース一覧を追い、検証してから更新する体制を、導入より先に用意してください。更新の判断手順はKeycloakの脆弱性対応の手順で解説しています。

社員だけが使うSaaS中心の環境ではKeycloakを入れず見送る条件

使うのが社員だけで、対象がMicrosoft 365やSalesforceなどのSaaSばかりなら、Keycloakは入れません。それぞれのSaaSをEntra IDやOktaに直接登録すれば、条件付きアクセスもアプリ単位で効きます。Keycloakを挟むと、IDaaSからは配下の全アプリが1つのアプリに見え、アプリごとの制御が利かなくなります。

自社開発のアプリが1つだけの場合も同じで、そのアプリをIDaaSに直接登録すれば済みます。Keycloakを置くかどうかの判断から、クライアント設計、ログアウト連携の実装、既存システムの移行までまとめて任せたい場合は、一創の認証基盤・ID管理システム開発で支援しています。

Keycloak SSOの設定で導入担当者からよく受ける質問と回答

Keycloakで複数アプリのSSOを組むときに出やすい疑問をまとめました。

SAMLのアプリとOIDCのアプリを同じSSOに入れられますか?

入れられます。同じレルムにSAMLクライアントとOIDCクライアントを並べれば、どちらから先にログインしても、もう一方ではログイン画面が出ません。SSOのセッションはプロトコルに関係なくレルムに1つです。SAMLクライアントの登録とName ID・署名の設定はSAML 2.0 IdPの構築手順で扱っています。ログアウトはSAML側のSingle Logoutの設定も必要になる点が、OIDCだけの構成との違いです。

別のレルムに登録したアプリ同士でSSOさせる方法はありますか?

レルムをまたぐとSSOのセッションは共有されません。必要なら、片方のレルムをもう一方のレルムのIdentity providersにOIDCで登録し、ブローカー経由でログインさせます。ただし利用者の母集団が同じなら、レルムを1つにまとめるほうが設定も運用も単純です。レルムを分けたのは管理者や利用者を分けるためだったはずなので、SSOが必要になった時点で分割の理由を見直します。

SSO Session Idleを過ぎるとアプリ側では何が起きますか?

KeycloakのSSOセッションが消えるため、アプリが持つリフレッシュトークンでの更新は失敗します。アプリが利用者をもう一度送る先はKeycloakの認可エンドポイントで、表示されるのはログイン画面です。アクセストークンの有効期限内であれば、APIの呼び出しはしばらく通ります。切れた瞬間に全アプリで操作を止めたいなら、アクセストークンを短く保ち、更新の失敗をアプリ側で検知して再ログインへ回す実装にします。

Remember Meを有効にするとセッションはどれだけ延びますか?

レルムでRemember Meを有効にすると、SSO Session Idle Remember MeとSSO Session Max Remember Meの2項目が使えるようになります。この2項目の値が適用されるのは、利用者がログイン画面でRemember Meにチェックを入れたときだけです。管理ガイドによると、値が0以下なら通常のIdleとMaxと同じ時間になるため、延ばしたい長さを明示的に入れます。共有端末を使う業務では、有効にしないほうが安全です。

ブラウザだけで動くSPAでもバックチャネルログアウトは使えますか?

SPAにはLogout Tokenを受けるサーバーが無いため、バックチャネルは使えません。管理ガイドは、ブラウザで動くクライアントはSSOセッションがログアウトされたことを自分で検知するので、Keycloakから通知を送る必要は無いと説明しています。SPAの登録はpublic clientで、PKCEは必須です。SPAの裏にBFFのようなサーバーを置く構成なら、そのサーバーをconfidentialクライアントにしてバックチャネルで受けます。

関連記事

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

一創の公式 SNS

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

この記事は以下の記事からリンクされています

資料請求

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

  1. 2026.11.05 テックブログ Linuxカーネルの脆弱性CVE-2026-98432:修正版の見分け方とnftablesの緩和策
  2. 2026.11.04 テックブログ CVE-2026-119977:WordPress更新中のRCEと安全な更新手順
  3. 2026.11.03 テックブログ クラブツーリズム不正アクセス:約432万件の対象確認と偽メールの見分け方・流出検知の設計
  4. 2026.10.29 テックブログ ソニー生命の情報漏洩38.5万件|ログインできない理由、再開状況と窓口の電話番号
  5. 2026.11.05 コラム 給与支払報告書とは?令和8年分の提出期限・書き方と源泉徴収票みなし提出への対応

RELATED POSTS 関連記事

目次