前回はカスタムMapper の作成と登録について記載しました。
今回はカスタムAuthenticator についてです。
認可URL に追加されたパラメータ device_id=xxx の値を読み取り、クライアントセッションに格納する処理になります。
Authenticator の作成
前回作成したJava プロジェクトにクラスDeviceIdAuthenticatorFactory とDeviceIdAuthenticator を追加します。
DeviceIdAuthenticatorFactory は特筆すべきことはありません。こちらのサンプル(再掲)を参考にしてください。
DeviceIdAuthenticator はインターフェースAuthenticator を実装します。
重要なのはこの部分です。
@Override
public void authenticate(AuthenticationFlowContext context) {
// パラメータからdevice_id を取得
String deviceId = context.getHttpRequest().getUri().getQueryParameters().getFirst("device_id");
if (deviceId != null && !deviceId.isBlank()) {
// クライアントセッション(デバイス毎のセッション)にdevice_id を保存
logger.debugf("Device ID captured in authenticator: %s", deviceId);
context.getAuthenticationSession().setClientNote("device_id", deviceId);
context.success();
} else {
// 取得できなければエラーとして認証処理を中断させる
logger.warn("Missing device_id parameter in authentication request");
context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
}
}また、Mapper と同じように、作成したFactory クラス完全名を META-INF/services/org.keycloak.authentication.AuthenticatorFactory に記載します。
com.example.sample.keycloak.DeviceIdAuthenticatorFactoryjar の作成やKeycloak へのインストール手順は前回と同じです。
Keycloak のAuthentication flow へ登録
Keycloak ではログイン処理がAuthentication flow と呼ばれるステップで構成されています。一から作成するのは大変なので、browser の認証フローをコピーしたものをカスタマイズしてきます。
サイドメニューのAuthentication からbrowser の右側のメニューよりDuplicate を選びます。

Name をbrowser with device-id とします。

これで認証フローのコピーが作成されました。

上の画像は最上位のトグルを閉じた状態になっています。
Authentication flow の簡単な説明
Requirement としてRequired(必須)、Alternative(いずれか1つ必須)、Disabled(無効)の3つがあります。
現在の状態は、Cookie、Identity Provider Redirector、Organization、forms が同じレベル(最上位)に並んでおり、いずれか1つ成功すれば次に進むという意味になります。
Kerberos はDisabled なので無効です。
Cookie は一度ログインされたセッションを持っていた場合、ログイン画面を出さずに認証が通ったものとして扱うものです。
Identity Provider Redirector はGoogle sign in 等の外部OIDC 連携を行っている場合にリダイレクトを行うものです。今回は使っていないのでDisable にしても良いです。
Organization は組織ごとにログイン画面を変更する場合に使うそうです。こちらも今回は使っていないのでDisable にしても良いです。
forms はログイン画面を表示します。同じレベルでAlternative として登録されているCookie が満たされなかった場合に、ログイン画面が表示されます。今回はここのサブフローとして作成したカスタムAuthenticator を追加します。
Authentication flow へ登録つづき
画面上部のAdd execution をクリックします。

device で検索を行い、Device ID Capture Authenticator にチェックを入れてAdd をクリックします。

追加直後の画面です。閉じていたトグルが全て開いた状態になってしまいます。(少し焦ります)

再度トグルを閉じると、一番下に追加されていることが分かります。

このままでは、最上位にRequiredとして配置されいるため、先ほど紹介したCookie が使えない問題が起きます。(1敗)
もう少し細かくお話しすると、この状態ではセッションが確立されていたとしてもdevice_id をパラメータとして毎回送付しなければエラーになってしまいます。Cookie がAlternative でDevice ID がRequired として同列で並ぶため、Cookie が成功してもDevice ID が必須となるためです。
左側の点々をドラッグできるので、なんとかしてforms 配下の一番上にDevice ID を配置します。
ドラッグドロップが多少不安定(バグ?)なので何度か試行錯誤が必要です。ctrl キーとマイナスを何度か押して、画面を大きくすると動かしやすいかもしれません。
最終的に下の画像の構成にします。

最後にAuthentication 一覧からbrowser with device-id をBrowser flow としてBind します。

Authenticator 一覧画面を見ると、used by にチェックが入っています。

以上でKeycloak の設定は完了です。
取得されたアクセストークンを解析
前回と同じように、認証完了後に取得できるトークンを解析します。

device_id としてswift から送付した文字列deivce-ios が追加されていることが分かります。
感想
私はAuthentication flow のRequired とAlternative の意味を分からずに、最上位にRequired として登録していたため、いつまでもCookie が通らずに苦戦しました。
ちゃんと理解しないとダメですね。。。(笑)















