Keycloakの認証フロー(Authentication Flows)は、ログイン画面でどの順に何を確かめるかを決める設定です。既定のままでは、ワンタイムパスワード(OTP)は認証アプリを登録した利用者にだけ求められ、未登録の利用者はパスワードだけで入れます。本記事では2026年11月6日時点の最新版Keycloak 26.8.2を前提に、既定のブラウザフローの読み方、複製して特定ロールの利用者だけOTPを必須にするkcadm.shのスクリプト、アプリ単位の上書き、Required ActionsでOTPの登録を求める設定を順に解説します。
まとめ:Keycloakの認証フローは既定を複製して条件付きサブフローで組み替える
組み込みのブラウザフローは直接編集できないため、複製してから手を入れます。既定の「Browser – Conditional 2FA」は、OTPを登録済みの利用者にだけOTPを求める作りです。管理者など特定の利用者に必ずOTPを使わせたいなら、複製したフローにロール条件付きのサブフローを足し、OTPフォームをRequiredにします。未登録の対象者は、そのログインの途中でOTPの登録を求められます。
全員にOTPを求めるだけなら、フローの複製は不要です。Required Actionsの「Configure OTP」を既定アクションにし、既存ユーザーにも付ければ済みます。アプリ単位でフローを差し替える上書き設定は、SSO済みの利用者には効きません。管理画面だけ強い認証を求めたい場合は、ステップアップ認証で組みます。
Keycloakの認証フローの構造と既定のブラウザフローでOTPが任意になる理由
ここで扱うのはKeycloakの管理画面にある「Authentication」の設定です。OIDCの仕様が定める認可コードフローやインプリシットフローとは別物で、そちらはOIDCの認証フローとはで解説しています。
フロー・サブフロー・実行ステップの3層とRequirementの4つの評価規則
Server Administration GuideのAuthentication flowsは、認証フローを、ログインや登録の際に行う認証・画面・処理の入れ物と説明しています。トップレベルのフローの中にサブフローと実行ステップ(Execution)が並び、サブフローの中にもさらに入れ子にできます。
各要素の動きはRequirementの設定で決まります。
| Requirement | 評価のされ方 |
|---|---|
| Required | 同じ階層の全てが順に成功する必要がある |
| Alternative | どれか1つが成功すればよい |
| Disabled | 成功の判定に数えない |
| Conditional | サブフロー専用・全条件が真でRequired扱い |
見落としやすいのは2点です。同じ階層にRequiredがあると、Alternativeの要素は実行されません。Conditionalのサブフローは、条件が1つでも偽になるか、条件が1つも置かれていない場合にDisabled扱いになります。
26.8.2の既定ブラウザフローの中身と2要素目が登録済みの利用者にだけ出る条件
既定のブラウザフローの構成は、26.8.2のDefaultAuthenticationFlows.javaで確かめられます。新しいレルムに作られる並びは次のとおりです。
| 階層 | 実行ステップ・サブフロー | 既定のRequirement |
|---|---|---|
| 1 | Cookie | Alternative |
| 1 | Kerberos | Disabled |
| 1 | 外部IdPへのリダイレクト | Alternative |
| 1 | forms(サブフロー) | Alternative |
| 2 | Username Password Form | Required |
| 2 | Browser – Conditional 2FA | Conditional |
| 3 | 利用者の設定済み条件 | Required |
| 3 | Condition – credential | Required |
| 3 | OTP Form | Alternative |
| 3 | WebAuthn Authenticator | Disabled |
| 3 | リカバリーコード入力フォーム | Disabled |
表内の「外部IdPへのリダイレクト」の正式名称はIdentity Provider Redirector、「利用者の設定済み条件」はCondition – user configured、「リカバリーコード入力フォーム」はRecovery Authentication Code Formです。OTPが任意になる理由は「Condition – user configured」による条件判定です。この条件は、同じサブフローの実行ステップが利用者向けに設定済みかを確かめるため、OTPを登録していない利用者では偽になり、2要素目の画面ごと飛ばされます。「Condition – credential」は、パスキー(パスワードレスのWebAuthn)でログインした場合に2要素目を省く条件で、パスキー機能が有効な場合だけ置かれます。WebAuthnを2要素目に使う設定はWebAuthnとはで扱いました。
ブラウザフローを複製してロールを持つ利用者だけOTPを必須にするkcadm.shの設定
例として、レルムcorpで、レルムロールmfa-requiredを持つ利用者にだけOTPを必ず使わせます。OTPを登録済みの一般の利用者には、既定どおりOTPを求めます。
組み込みフローを編集できない制約と複製したときに付くサブフロー名の規則
管理ガイドは、組み込みのフローは変更できず、要素のRequirementだけ変えられると書いています。26.8.2のAuthenticationManagementResource.javaを読むと、組み込みのフローにステップやサブフローを足すAPIは400を返します。
複製のAPIで新しい名前を受け取るパラメーターは、newNameです。中のサブフローは「新しい名前+半角スペース+元の名前」に改名されるため、browser-mfaとして複製するとbrowser-mfa formsとbrowser-mfa Browser - Conditional 2FAができます。スクリプトでサブフローを指定するときは、この名前をURLエンコードして使います。
kcadm.shで条件付きサブフローとロール条件・OTPフォームを足すスクリプト
エンドポイントはAdmin REST APIのauthentication/flows配下です。条件の設定キーは各Factoryクラスの定数で確かめました。
#!/usr/bin/env bash
# Keycloak 26.8 系:browser を複製し、mfa-required ロールの利用者だけ OTP を必須にする
# 前提:kcadm.sh と jq が使えること。ロール mfa-required は作成済み
set -euo pipefail
REALM=corp
FLOW=browser-mfa
FORMS="${FLOW}%20forms"
SUB_NAME="${FLOW} mfa-required-otp"
SUB="${FLOW}%20mfa-required-otp"
kcadm.sh config credentials --server https://auth.example.com --realm master --user admin
# 1. 既定の browser フローを複製する
kcadm.sh create authentication/flows/browser/copy -r "$REALM" -s newName="$FLOW"
# 2. forms の末尾に条件付きサブフローを足す(既定の Conditional 2FA より後ろに入る)
kcadm.sh create "authentication/flows/${FORMS}/executions/flow" -r "$REALM" \
-s alias="$SUB_NAME" -s type=basic-flow -s description="mfa-required は OTP 必須"
# 3. サブフローに条件 2 つと OTP フォームを足す
for P in conditional-user-role conditional-sub-flow-executed auth-otp-form; do
kcadm.sh create "authentication/flows/${SUB}/executions/execution" -r "$REALM" -s provider="$P"
done
# 4. 条件の設定(ロールを持つ、かつ既定の 2FA が実行されなかった場合だけ真)
exec_id() {
kcadm.sh get "authentication/flows/$1/executions" -r "$REALM" \
| jq -r --arg p "$2" '.[] | select(.providerId==$p or .displayName==$p) | .id'
}
kcadm.sh create "authentication/executions/$(exec_id "$SUB" conditional-user-role)/config" -r "$REALM" \
-b '{"alias":"mfa-required-role","config":{"condUserRole":"mfa-required","negate":"false"}}'
kcadm.sh create "authentication/executions/$(exec_id "$SUB" conditional-sub-flow-executed)/config" -r "$REALM" \
-b "{\"alias\":\"no-2fa-yet\",\"config\":{\"flow_to_check\":\"${FLOW} Browser - Conditional 2FA\",\"check_result\":\"not-executed\"}}"
手順4の2つ目の条件が要です。Condition – sub-flow executedにnot-executedを指定すると、既定の2FAのサブフローが成功しなかった場合だけ真になります。これを入れないと、OTPを登録済みの対象者は2回OTPを聞かれます。この組み方は、管理ガイドの2FA conditional workflow examplesにある「OTP default」の例に、ロール条件を足したものです。
追加したステップの既定がDisabledになる仕様とRequirementの書き換え
APIで足したサブフローと実行ステップは、選べるRequirementが1つしかないものを除いて、Disabledで作られます。このままでは何も起きないので、次のように書き換えます。
# 5. Requirement を設定する(GET した要素を丸ごと送り返す)
set_req() {
kcadm.sh get "authentication/flows/$1/executions" -r "$REALM" \
| jq -c --arg p "$2" --arg r "$3" '.[] | select(.providerId==$p or .displayName==$p) | .requirement=$r' \
| kcadm.sh update "authentication/flows/$1/executions" -r "$REALM" -n -f -
}
set_req "$FORMS" "$SUB_NAME" CONDITIONAL
set_req "$SUB" conditional-user-role REQUIRED
set_req "$SUB" conditional-sub-flow-executed REQUIRED
set_req "$SUB" auth-otp-form REQUIRED
# 6. レルムのブラウザフローを差し替える(動作確認の後に実行する)
kcadm.sh update realms/"$REALM" -s browserFlow="$FLOW"
idとrequirementだけを送る書き方は避けます。更新処理はpriorityの値も比べて書き換えるため、省くと並び順が変わるおそれがあります。-nは、取得と統合を省いてそのまま送る指定です。
OTPフォームをRequiredにした場合、OTPを登録していない対象者はログインの途中で登録画面に進みます。管理ガイドは、2要素目をRequiredにすると登録した直後の認証にその要素が使われない点を挙げ、1要素目にパスワードなどを置くよう求めています。この例はパスワードの後ろに置いているので、その条件を満たす構成です。
ブラウザフローを割り当てる前に3種類の利用者で確かめる動作確認
手順6の割り当てを最後に回すのは、誤った設定のまま全員のログインを止めないためです。管理コンソールのbrowser-mfaの画面で並びとRequirementを確かめたら、検証用の利用者を3人用意します。
- ロールなし・OTP未登録:パスワードだけでログインできる
- ロールあり・OTP未登録:パスワードの後にOTPの登録画面が出る
- ロールあり・OTP登録済み:OTPの入力が1回だけ求められる
3の利用者でOTPが2回出るなら、flow_to_checkの名前が複製後のサブフロー名と一致していません。割り当て後に問題が出た場合はbrowserFlow=browserに戻せば、既定のフローに切り替わります。管理者自身がロックアウトされないよう、masterレルムの管理者で作業します。
アプリ単位で認証フローを変えるクライアントの上書き設定と効かない場面
レルム全体ではなく、特定のアプリにだけ別のフローを使わせたい場面もあります。クライアントの詳細画面のAdvancedタブにある認証フローの上書きで設定します。
クライアント単位の認証フロー上書きと名前ではなくIDを指定する設定
上書きはクライアントのauthenticationFlowBindingOverridesに、種類ごとのフローのIDを入れる形で持ちます。種類のキーはbrowserとdirect_grantの2つです。
# 管理画面のクライアント admin-portal だけ browser-mfa を使わせる
FID=$(kcadm.sh get authentication/flows -r corp | jq -r '.[] | select(.alias=="browser-mfa") | .id')
CID=$(kcadm.sh get clients -r corp -q clientId=admin-portal --fields id --format csv --noquotes)
kcadm.sh update clients/"$CID" -r corp -s "authenticationFlowBindingOverrides={\"browser\":\"$FID\"}"
値はフローの名前ではなくIDです。フローを作り直すとIDが変わるため、上書き側も入れ直します。クライアントの作成や更新をAPIで行う書き方はKeycloak APIの使い方にまとめました。
上書きしたフローがSSO済みの利用者に再認証を求めない理由とステップアップ
複製したフローの先頭にはCookieがAlternativeで残っています。管理ガイドによると、Cookieはセッションのクッキーがあれば成功し、同じ階層の他の要素は実行されません。つまり、別のアプリでパスワードだけでログインした利用者が管理画面を開くと、上書きしたフローを通っても、OTPを聞かれずに入れます。SSOの仕組み自体はKeycloak SSOの設定手順で説明しています。
アプリごとに認証の強さを変えたいなら、管理ガイドのstep-upの手順で組みます。「Condition – Level of Authentication」で段階を分け、2段目にOTPフォームを置き、Max Ageを0にすると、その段階を要求したログインのたびにOTPを求められます。アプリは認可リクエストのclaimsまたはacr_valuesで段階を要求し、レルムの設定でACRの値と段階の数字を対応させる仕組みです。アプリを並べる時点で強さの違いが見えているなら、上書きよりステップアップで設計するほうが漏れません。
Required ActionsでOTPの登録とパスワード変更を初回ログイン時に求める設定
認証フローが「ログインで何を確かめるか」を決めるのに対し、Required Actionsは「ログインの後に何をさせるか」を決めます。管理ガイドのRequired actionsの節は、資格情報を入れた後に実行させる操作と説明しています。
主なRequired Actionsの識別子と既定アクションが新規ユーザーにだけ効く範囲
| 画面の名前 | 識別子 | 利用者にさせること |
|---|---|---|
| Configure OTP | CONFIGURE_TOTP | 認証アプリの登録 |
| Update Password | UPDATE_PASSWORD | パスワードの変更 |
| Verify Email | VERIFY_EMAIL | メールのリンクで所有確認 |
| Update Profile | UPDATE_PROFILE | 氏名などの更新 |
| Terms and Conditions | TERMS_AND_CONDITIONS | 利用規約への同意 |
Required ActionsタブのSet as default actionを有効にすると、そのアクションは新しく作るユーザーに付きます。管理ガイドが対象として挙げるのは、管理画面のAdd userで作ったユーザーと、ログイン画面の登録リンクから作られたユーザーです。既に存在するユーザーには付かないため、次の項の一括付与が必要になります。
既存ユーザーにCONFIGURE_TOTPを付けるkcadm.shのループと上書きの注意
# 新規ユーザーに OTP の登録を既定で求める
kcadm.sh update authentication/required-actions/CONFIGURE_TOTP -r corp -s enabled=true -s defaultAction=true
# 既存ユーザーにも付ける(requiredActions は配列ごと置き換わる点に注意)
for U in $(kcadm.sh get users -r corp --fields id --format csv --noquotes -l 1000); do
kcadm.sh update users/"$U" -r corp -s 'requiredActions=["CONFIGURE_TOTP"]'
done
requiredActionsは配列ごと置き換わるため、メール確認などが残っているユーザーでは元のアクションが消えます。運用中のレルムでは、先に各ユーザーの値を取得し、足してから送る手順が必要です。1,000人を超えるレルムでは-oと-lで区切って回します。
この方式とロール条件のフローでは、受け持つ役割が別々です。Required Actionsは1回登録させれば終わりで、毎回のOTP入力は既定の「Browser – Conditional 2FA」が受け持ちます。全員にOTPを使わせる方針なら、フローを複製せずこの方式で足ります。
OTPポリシーの既定値6桁・30秒・SHA1と前後の時間ずれを許す窓
認証アプリの設定は、AuthenticationのOTP Policyタブで決めます。26.8.2のOTPPolicy.javaの既定値は、時刻ベースのTOTP・HmacSHA1・6桁・look around 1・30秒です。
管理ガイドによると、look aroundが1で30秒周期なら、前後を含めて90秒の範囲のコードを受け付けます。同じコードの再利用は既定で認められていません。ハッシュをSHA256以上に変える場合は、利用者が使う認証アプリが対応しているかを先に確かめます。総当たり対策の対象には、パスワードと並んでOTPとリカバリーコードが含まれます。
認証フローを作り込んだ後に運用で詰まる場面と既定のまま使う判断
認証フローは画面から自由に組めるぶん、作った後の保守が見落とされがちです。導入前に次の3点を決めておきます。
複製したフローはバージョンアップで既定の変更を取り込まないという保守の注意
複製したフローは、複製した時点の中身のまま残ります。管理ガイドのOrganizationsの既存フローの節は、新しいレルムでは自動で足されるステップを、既存のカスタムフローには手で足すよう求めています。パスキー用の「Condition – credential」も、既定フローにだけ入る種類の変更です。
KeycloakはGitHubの公開日(UTC)で26.8.0が10月1日、26.8.1が10月13日、26.8.2が11月5日と、短い間隔で版が出ます。更新のたびに、新しいレルムの既定ブラウザフローと自社の複製フローをkcadm.sh get authentication/flows/browser/executionsで並べて比べる手順を、更新作業の一覧に入れてください。更新の判断手順はKeycloakの脆弱性対応の手順で解説しています。
ロール条件は1つしか指定できない制約と複合ロールで束ねる設計
Condition – user roleに入れられるロールは1つです。Keycloakのフォーラムでも、複数のロールを対象にできないという相談が出ています。ロールごとに条件付きサブフローを並べると、フローが読めなくなります。
対象を増やすときは、mfa-requiredのような判定専用のロールを1つ作り、管理者ロールや経理のグループにその複合ロールとして付けます。条件はユーザーが持つロールで判定されるため、グループ経由や複合ロール経由で付いたロールも判定結果は真です。条件の設定画面にあるnegateを使えば、「このロールを持つ人だけOTPを免除する」逆向きの指定もできます。
カスタムAuthenticatorを書かずに済ませる条件と開発が必要になる場面
OTP・WebAuthn・リカバリーコードの組み合わせ、ロールや属性での分岐、未登録者の拒否までは、標準の実行ステップで組めます。管理ガイドには、2要素を使っていない利用者を「Deny Access」で止める例も載っています。この範囲でJavaのコードを書くのは過剰です。
一方、SMSでコードを送る、社内の勤怠システムの在籍情報で分岐する、といった要件は管理ガイドの手順に載っていません。この場合はAuthenticator SPIで実行ステップを自作し、Keycloakの版を上げるたびに再ビルドと検証を回す体制が要ります。2要素の方式選びから、フローの設計、カスタムAuthenticatorの開発と保守までまとめて任せたい場合は、一創の認証基盤・ID管理システム開発で支援しています。
Keycloakの認証フロー設定で導入担当者からよく受ける質問と回答
認証フローを組み替えるときに出やすい疑問をまとめました。
既定のブラウザフローで全員にOTPを必須にするにはどうしますか?
選択肢は、フローを複製する方法と、Required Actionsを使う方法の2つです。複製する場合は「Browser – Conditional 2FA」をRequiredに変え、中のOTP FormもRequiredにします。条件が評価されなくなるため、未登録の利用者はログインの途中で登録を求められます。新規ユーザーだけでなく既存ユーザーにも登録させたいなら、Configure OTPを既定アクションにして一括で付ける方法のほうが、フローを保守する手間がかかりません。
OTPを登録していない管理者のログインを止めることはできますか?
できます。管理ガイドの「Conditional 2FA sub-flow and deny access」の例では、既定の2FAのサブフローの後ろに、Condition – sub-flow executedでnot-executedを判定するサブフローを置き、中にDeny Accessを入れます。本記事のスクリプトのOTPフォームをDeny Accessに置き換えれば、ロールを持つ未登録者はエラー画面で停止する動作です。エラーの文言は実行ステップの設定とログインテーマのメッセージで変えられます。ログインテーマの文言を上書きする手順はKeycloak ログイン画面のカスタマイズ手順:テーマ作成・ロゴ差し替えと日本語メッセージの上書きで扱いました。
外部IdPでログインした利用者にも認証フローは適用されますか?
外部IdPで認証した利用者は、ブラウザフローのパスワード欄の後ろに置いたOTPを通りません。戻った後に使われるのは、外部IdPの設定に紐づくFirst login flowとPost login flowです。外部IdPの多要素認証を信頼するなら追加の設定は要りません。Keycloak側でもOTPを求めたい場合は、OTPフォームだけを置いたフローを作り、外部IdPの設定のPost login flowに指定します。初回ログインでの既存アカウントとの紐付けはKeycloak SAML設定:Entra IDを外部IdPにする手順で扱いました。
パスキーを導入するとOTPの認証フローはどう変わりますか?
既定のフローでは、パスキーでログインした利用者に2要素目を求めない「Condition – credential」が働きます。管理ガイドのpassword-less browser login flowの例は、パスキーとパスワード+OTPを選べる構成です。Enable PasskeysとCondition – credentialの設定はKeycloak パスキーの設定手順:Enable Passkeysとパスワードレスポリシー・登録の流れで扱いました。パスワードからパスキーへ段階的に切り替える進め方はパスキー移行の実装手順で解説しています。
作った認証フローを別の環境へ移すにはどうしますか?
フローはレルムの設定の一部なので、レルムのエクスポートに含まれます。ただし、実行ステップの設定にはIDが振られ、クライアントの上書きもフローのIDで持つため、環境ごとにIDが変わる点には注意が必要です。本記事のように名前で組み立てるスクリプトを用意しておけば、検証環境と本番に同じフローを再現できます。2要素認証の方式の違いや社内への説明の仕方は二段階認証とはが参考になります。
関連記事
- Keycloakとは?Oktaとの違いと連携手順|Auth0・Cognito比較で導入を判断【2026年10月】:Keycloakの機能の全体像と、Oktaなど他製品との違いです。
- Keycloak SSOの設定手順:複数アプリのOIDCクライアント登録とセッション・ログアウト連携:認証フローを通った後のSSOセッションとログアウトの設定です。
- Keycloak APIの使い方:Admin REST APIでユーザー作成とロール付与をcurlで行う手順:判定用ロールの付与をAPIで自動化する方法です。
- WebAuthnとは?パスキー認証の仕組みと登録・認証フローをコードで解説:OTPの代わりに2要素目に使える方式の仕組みです。
- OIDCの認証フローとは?3つのフローとresponse_typeの対応・移行判断を実装視点で解説:名前が同じプロトコル側の認証フローの解説です。