【ITニュース解説】What Contributing to Two Keycloak Projects Taught Me About Building Authentication Extensions
2026年10月19日に「Dev.to」が公開したITニュース「What Contributing to Two Keycloak Projects Taught Me About Building Authentication Extensions」について初心者にもわかりやすく解説しています。
ITニュース概要
Keycloakの認証拡張開発で、筆者は重要な教訓を得た。機能する認証方法が選択肢に表示されない問題、登録処理の不備、ライブラリ不足による実行時エラーに直面。フレームワークの挙動理解と、本番環境を意識したテストの重要性を指摘する。
ITニュース解説
Keycloakは、ユーザーのログインやアクセス権限を管理するIDおよびアクセス管理サーバーだ。これはシングルサインオン機能も提供し、開発者は「Service Provider Interface(SPI)」という仕組みを使って、認証プロセスを拡張したり新しい認証方法を追加したりできる。たとえば、多要素認証(MFA)の一つであるワンタイムパスワード(OTP)をメールで送信する機能などをKeycloakに追加できる。
筆者は、メールやメッセージングアプリを使った2段階認証(2FA)のプロジェクトに貢献した経験を通じて、システムを開発する上で見落とされがちな、しかし極めて重要な「境界線」に気づかされた。
まず、認証機能自体は正しく動作するのに、ユーザーがログイン時にその方法を選択できないという問題に直面した。メールOTP機能は、単独で設定されていれば動いたが、パスワードログインなど他の認証方法と併用されると、「別の方法を試す」という選択肢の中に表示されなかったのだ。 この原因は、OTPの生成や検証を行うコードにはなく、Keycloakがどのように認証器を分類し、ユーザーに表示する選択肢リストを決定するかというフレームワーク側のロジックにあった。Keycloakは、認証方法を選択肢として表示する際、「AuthenticationSelectionResolver」という仕組みを使っていた。これは、資格情報を検証する役割を持つ「CredentialValidator」インターフェースを実装する認証器を、その「資格情報タイプ」でインデックス作成し、ログインユーザーが持つ「保存済みの資格情報」やユーザー情報システムが報告する「設定済みタイプ」と照合して選択肢を絞り込んでいた。 筆者のメールOTP拡張機能には、事前に登録しなくてもメールアドレスがあればOTPを使えるようにする「skipSetup」オプションがあった。しかし、このオプションは、認証器が「ユーザー向けに設定済み」と判断する内部ロジックには影響を与えたものの、Keycloakが選択肢リストを構築する際の資格情報照合パスには影響しなかった。そのため、ユーザーがメールOTPの資格情報を明示的に保存していなかったりすると、選択肢として表示されなかった。 この問題を解決するため、筆者は二つの異なる振る舞いを、それぞれ別の認証器として分離した。「EmailAuthenticatorForm」は登録済みユーザー向けに、「NoEnrollmentEmailAuthenticatorForm」は、事前に登録しなくてもメールアドレスがあればOTPを使えるようにするものだ。後者はCredentialValidatorを実装しないことで、資格情報ベースの選択パスから外れ、未登録ユーザーでも選択肢として表示されるようになった。この経験から、拡張機能の個々の動作だけでなく、フレームワーク全体がどのように拡張機能を呼び出すのかを追跡することの重要性を学んだ。
次に、メッセージング拡張機能の「登録」プロセスで、ユーザーがセットアップを「再度」行った場合に問題が発生した。これまでの実装では、セットアップを繰り返すたびに新しいメッセージング資格情報が作成され、重複エラーや古い連絡先情報がシステムに残ってしまう問題が生じた。 筆者の修正は、この「再セットアップ」を、既存のメッセージング設定を「更新する」振る舞いとして捉え直すことだった。具体的には、この拡張機能に対しては常に一つのメッセージング資格情報のみを維持し、既存の資格情報があればそれを更新し、なければ新しく作成するようにした。更新の際には、余分なメッセージング資格情報があれば削除するクリーンアップ処理も加えた。 また、関連する問題として、セットアップフォームの表示があった。テンプレートでは、変数が「存在するか」だけを見ており、その「真偽値」を正しくチェックしていなかったため、Telegram以外のチャネルでもTelegram用の表示が選ばれることがあった。筆者はこのテンプレートの条件式を修正し、真偽値を正しくチェックするように変更した。さらに、エラー発生時のフォーム再表示でもチャネルに関する属性が適切に供給されないことがあったため、フォーム準備処理を中央集約することで、初回表示時もエラー再表示時も、常に同じチャネル情報がフォームに供給されるようにした。
三つ目は、ビルド自体は成功するものの、実際にデプロイすると認証拡張機能が動作しないという問題だ。メッセージング拡張機能で「java.util.ServiceConfigurationError」というエラーが発生した。原因は、ビルド成果物のJARファイルに、必要なAWS SDKのクラスが含まれていなかったことだ。テスト環境ではMavenのテストクラスパスにAWS SDKが存在したため、テストは成功し、問題が隠蔽されていた。JavaのServiceLoaderがプロバイダをロードする際に、参照しているAWS SDKのクラスが見つからないとエラーが発生し、それが「ServiceConfigurationError」としてラップされ、誤解を招くメッセージとして表示されていたのである。 このプロバイダのロード失敗は他のメッセージングプロバイダの選択にも影響を与えていたため、筆者は問題を二つの側面から解決した。一つは、ビルドプロセスにおいて必要なAWS SDKの依存関係をプロバイダJARに適切にパッケージングすること。もう一つは、プロバイダのロード失敗が他のプロバイダに影響しないよう、レジストリで個々のプロバイダのロード時に発生するエラーを捕捉し、ログに記録して処理を続行できるようにしたことだ。 この問題の解決において特に重要だったのは、テスト方法の見直しだった。筆者は、パッケージング後のJARファイルの内容を検証する統合テストと、隔離されたクラスローダーを使って、パッケージングされたプロバイダが実際にKeycloakサーバーが提供するライブラリだけでインスタンス化できるかを検証する統合テストを追加した。同様に、CI(継続的インテグレーション)環境でも古いKeycloakバージョンとの互換性問題があったため、CIビルドに最も古いサポート対象のKeycloakバージョンに対するテストを追加し、互換性問題を早期に発見できるようにした。
これらの経験を通じて筆者が得た教訓は、認証拡張機能のようなシステムを開発・レビューする際には、いくつかの重要な視点を持つべきだということだ。第一に「選択」の視点。認証方法がユーザーにどのように選択可能になるのか、フレームワークがどのようにその選択肢を提示するのかを理解すること。第二に「登録」の視点。ユーザーがセットアップを繰り返したり、連絡先を変更したり、無効な入力をしたりした場合に、システムがどのように振る舞うのかを検証すること。第三に「パッケージング」の視点。成功したビルドを盲信せず、実際にインストールされる成果物が何を含み、どの環境・バージョンに対してビルドされたのかを徹底的に確認すること。これらの視点は、実際のログイン体験を横断する重要な境界線を示しており、システムの内部動作だけでなく、それがユーザーにどのように見え、どのように提供されるかを深く理解することが、堅牢な認証システムを構築する上で不可欠だ。