Keycloakのログイン画面を自社ブランドに寄せつつ、多言語対応し、なおかつバージョンアップで壊れないようにする。この3つを両立させるのがカスタムテーマ設計の勘所です。テーマの型(type)ごとの構成から、FreeMarkerテンプレの継承、messagesによる国際化、そしてaccount console v2/v3の扱いまで、実案件で踏んだ勘所を整理します。
01テーマの4つの型と全体像
Keycloakのテーマは「型(theme type)」という単位で構成されます。エンドユーザー向けのログイン画面を担うlogin、ユーザー自身が自分の情報を管理するaccount(アカウントコンソール)、管理者向けのadmin(管理コンソール)、通知メールのemail、そしてサーバー起動時のトップページであるwelcomeの5種類です。実務でブランディングの手が入るのは、ほぼ login / account / email の3つに集約されます。
各テーマは themes/<テーマ名>/<型>/ というディレクトリ構造を取り、その直下に theme.properties、FreeMarkerテンプレート(.ftl)、静的リソース(resources/)、翻訳ファイル(messages/)が配置されます。1つのテーマ名の下に複数の型をぶら下げられるので、たとえば mycompany というテーマ名でlogin/account/emailを揃える、という作り方が基本形になります。詳細は公式のWorking with themesが一次情報です。
02継承(parent)を前提に設計する
Keycloakのテーマ設計で最も重要な原則は、ゼロから作らず既存テーマを継承することです。公式ドキュメントでも「すべてのページを置き換えるつもりでない限り、別のテーマを拡張すべき」と明記されています。継承は theme.properties の parent プロパティで指定します。
base— HTMLの骨格とメッセージバンドルだけを持つ最小の土台。スタイルはほぼ持ちません。keycloak—baseを継承し、標準の見た目(CSS・画像)を乗せたデフォルトテーマ。- login以外では
keycloak.v2/keycloak.v3といった、後述するReactベースのコンソール向けの親も存在します。
継承の効果はテンプレートだけでなくメッセージ(翻訳)にも及びます。子テーマは親のメッセージバンドルをすべて継承し、必要なキーだけを上書きできます。これが後述のi18nを軽量に保つ鍵になります。自テーマには「差分だけ」を置く、という発想を徹底してください。
03FreeMarkerテンプレートの上書き
Keycloakは画面描画にApache FreeMarkerを使います。ログイン画面をカスタムしたい場合、標準テーマの themes/base/login/login.ftl を自テーマの login/ 配下にコピーし、必要な箇所だけ編集するのが定石です。テンプレート冒頭の <#import "template.ftl" as layout> で共通レイアウト(ヘッダー・フッター・ロケール選択など)を取り込む構造になっており、各画面はこの template.ftl の内側に差し込まれます。
ここで踏みやすい落とし穴が、「全部コピーしてしまう」ことです。login.ftl だけでなく register.ftl、login-reset-password.ftl、login-otp.ftl など画面ごとにテンプレートが分かれており、これらを丸ごと自テーマに抱え込むと、Keycloak本体のバージョンアップで標準テンプレに入った改善(セキュリティ修正、アクセシビリティ対応、新フィールド追加)が自テーマに反映されなくなります。公式も「組み込みテンプレートを可能な限り活用せよ」と推奨しています。
theme.properties のスタイル差し替えとCSS、ロゴ画像だけで達成できます。.ftl に手を入れるのは「標準テンプレでは構造的に不可能なとき」に限定する。これがアップグレード耐性を決定的に左右します。触った .ftl の本数は、そのまま将来の技術的負債の量だと考えてください。開発中はテーマキャッシュを無効化しておくと、ファイルを編集するたびに再起動せずに反映を確認できます。起動オプションは --spi-theme--cache-themes=false --spi-theme--cache-templates=false --spi-theme--static-max-age=-1 です(本番では必ずキャッシュ有効に戻すこと)。プロパティ名や指定方法はバージョンで変わり得るため、利用中のバージョンのServer Developer Guideで最終確認してください。
04静的リソースとtheme.propertiesの要点
CSS・JavaScript・画像は型ディレクトリ配下の resources/ に置きます。慣例として resources/css/、resources/js/、resources/img/ に振り分けます。theme.properties の主要プロパティは次のとおりです。
parent— 継承する親テーマ。styles— 読み込むCSSのスペース区切りリスト。標準テーマのstylesを上書きすると標準CSSが外れるので、追加なら親の値も含めて記述する。locales— サポートするロケールのカンマ区切りリスト(後述)。import— 別テーマからリソースを取り込む。
emailテンプレートだけは注意が必要です。メールはKeycloakのドメイン外(受信者のメールクライアント)で開かれるため、相対パスの画像は表示されません。ロゴなどを埋め込む場合は ${url.resourcesCommonUrl} のような絶対URLを使い、到達可能な公開URLでホストする必要があります。
05messagesによる多言語化(i18n)
画面上の文言はテンプレートに直書きせず、メッセージバンドルから ${msg("キー名")} で参照します。翻訳ファイルは <型>/messages/messages_<ロケール>.properties に置きます。日本語なら login/messages/messages_ja.properties です。ファイルはUTF-8で保存してください(読み込みに失敗するとISO-8859-1へフォールバックし、文字化けの原因になります)。
新しい言語を追加する手順は次のとおりです。
- login / account / email それぞれの
messages/messages_<ロケール>.propertiesを作成する。 - 各型の
theme.propertiesのlocalesにロケールコードを追加する(例:locales=en,ja)。 - ロケール選択メニューの表示名を
locale_ja=日本語の形式でメッセージに定義する。
プレースホルダは {0}、{1} の形式で、たとえば Log in to {0} の {0} にレルム名が入ります。継承のおかげで、標準で用意されている大量の翻訳キーはそのまま使え、自社で変えたいキーだけを上書きすればよい構造です。詳細は公式のLocalizationガイドを参照してください。
locales とレルム設定の対応言語が食い違うと、ロケール選択メニューが出ない/意図した言語に切り替わらない、という典型的なトラブルになります。両方を揃えるのが鉄則です。06account console v2 / v3 という別世界
ここが最もバージョン依存が大きく、設計判断を要する領域です。従来のaccount console(FreeMarkerベース)に対し、新しいアカウントコンソールはReactベースで作り直されており、テーマの拡張方法が根本的に異なります。新しめのバージョンではこのReact版が標準です。
Keycloakは @keycloak/keycloak-account-ui をnpmパッケージとして公開しており、これがReact版アカウントコンソールの土台になります。従来のように本体ソースをクローンして書き換える方式ではなく、公式パッケージを依存に取り込み、その上に自社コンソールを構築する形が推奨されています。プロジェクトの雛形は npm create keycloak-theme で生成できます。単純なブランディングであれば theme.properties で parent=keycloak.v3 を指定して継承するだけで済むケースもあります。
この移行の狙いは明快で、「本体のバージョンが上がってもコンソールが動き続ける」耐性を確保することです。逆に言えば、FreeMarker時代の感覚でテンプレを丸コピーして改造すると、v2/v3世代では通用しません。React版の作り込みを行うなら、公式のUsing the npm UI packagesを必ず確認してください。バージョンによって keycloak.v2 / keycloak.v3 の扱いや推奨手順が変わるため、利用中のバージョンでの確認は必須です。
07アップグレード耐性を最大化する設計指針
Keycloakは現在Quarkusベースが現行で、旧WildFlyベースは非推奨です。移行そのものはQuarkus版への移行で扱いますが、テーマに関してもディストリビューションの世代交代・マイナーアップグレードのたびに「壊れないか」が問われます。実務で効く指針を整理します。
- 継承で差分だけを持つ。
.ftlの丸コピーを避け、上書きは最小限に。触ったテンプレの数=将来の検証コスト。 - ブランディングはCSS・画像・messagesに寄せる。テンプレ構造に依存しない層で変更を吸収する。
- アップグレード前に差分を確認する。自テーマが上書きしている
.ftlと、新バージョンの標準.ftlを比較し、追加フィールドや構造変更を取り込む。KeycloakのGitHubのthemes/ディレクトリが比較の基準になります。 - account consoleはv2/v3方針を先に決める。FreeMarker改造とReactパッケージ利用は互換性がないため、世代を跨ぐ改修は作り直しになる前提で計画する。
- ステージングでテーマ回帰テストを回す。ログイン・登録・パスワードリセット・OTP・メール文面を、対応言語ぶん目視確認するチェックリストを用意しておく。
認証フロー自体をカスタムしている場合は、フローの変更が画面(テンプレ)に波及することがあります。あわせて認証フローの設計も確認しておくと、テーマとフローの整合が取りやすくなります。実際の構築事例は導入事例でも触れています。
—まとめ
Keycloakのカスタムテーマは、「型ごとに構成し、継承で差分だけを持ち、ブランディングはCSS・画像・messagesに寄せる」——この原則さえ守れば、ブランド体験・多言語対応・アップグレード耐性の3つを無理なく両立できます。逆に .ftl を安易に丸コピーすると、見た目は早く整っても、次のバージョンアップで負債が顕在化します。特にaccount consoleのv2/v3世代交代は方針を先に固めておくことが、後々の作り直しを避ける最大のポイントです。