KeycloakのApplication Initiated Actions (AIA) について

[NRI OpenStandia Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nri-openstandia)の5日目は、またまたKeycloakネタです。今日は **Application Initiated Actions** (略してAIAと呼ばれています) というまだまだ知名度の低いKeycloakの機能について紹介したいと思います。

# Application Initiated Actions (AIA) とは？

Keycloakをそこそこ触っている方でも本機能については聞いたことがないという方も多いんじゃないかなと思います。というのも、本機能はなぜかオフィシャルマニュアルにはまだ記載されていない機能のためです。開発の流れを追ってみると

* 2019/02 Stian氏により[開発者メーリングリスト(keycloak-dev)](https://lists.jboss.org/pipermail/keycloak-dev/2019-February/011737.html) にて提案され、[keycloak-communityリポジトリ](https://github.com/keycloak/keycloak-community/blob/master/design/application-initiated-actions.md)にもデザインドキュメントがコミットされる
* 2019/08 Keycloak 7.0.0に入る ([コミット](https://github.com/keycloak/keycloak/commit/6c79bdee41d7fb9b94fd0cef56db515b894f22a7)/[JIRAチケット](https://issues.redhat.com/browse/KEYCLOAK-9366)/[プルリクエスト](https://github.com/keycloak/keycloak/pull/6176))

と、1年前以上から存在する機能ではあるもののアンドキュメントなため、かなり知名度は低いと思われます。

# 何のための機能か

Keycloakには元々 **Required Actions** という機能があります。こちらは[ドキュメントに記載](https://www.keycloak.org/docs/latest/server_admin/index.html#required-actions)([日本語版](https://keycloak-documentation.openstandia.jp/master/ja_JP/server_admin/index.html#%E5%BF%85%E9%A0%88%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3))されているので、Keycloakをお使いの方はご存知かと思います。Required Actionsは認証完了後に必須実行させるアクション機能で、例えば認証時にユーザー自身にパスワードを強制変更させる、OTPやWebAuthnのセキュリティ・キーを登録させるといったケースで利用します。

Application Initiated ActionsはこのRequired Actionsと似ており、**任意のアプリケーション側からアクション実行を行うことができる** というものです。実行の起点が異なるだけで、動作するのは既存のRequired Actionsそのものです。

例えばアプリケーション内で、KeycloakのOTP/WebAuthnのセキュリティ・キーを登録する導線を作りたい場合に本機能を使うことができます。

Keycloakのエンドユーザ向けのアカウント管理画面が独立性の高いSPAで再実装されたため、このような別のアプリケーションからOTP/WebAuthnのセキュリティ・キーの登録などを呼び出す汎用的な仕組みとして開発された模様です。


# 使い方

マニュアルはありませんが、[デザインドキュメント](https://github.com/keycloak/keycloak-community/blob/master/design/application-initiated-actions.md)に記載があります。既存の認可コードフローを拡張して`kc_action`というパラメータを追加することで実現がされています。

```
 ../realms/myrealm/protocol/openid-connect/auth
    ?response_type=code
    &client_id=myclient
    &redirect_uri=https://myclient.com
    &kc_action=update_profile
```

上記のように`kc_action=update_profile`を追加した認可リクエストのURLをアプリケーションからリダイレクトさせることで、Keycloak側は指定されたアクションを実行する、という動作を行います。ちなみに、実際はこの`update_profile`は駄目で、`UPDATE_PROFILE`でないと駄目です。

# 使えるアクション

すべての既存のRequired Actionsが使えるわけではありません。Required Actionsの実装クラスで、`initiatedActionSupport`メソッドで`SUPPORTED`を返しているものが対象になります。

```
    InitiatedActionSupport initiatedActionSupport() {
        return InitiatedActionSupport.NOT_SUPPORTED;
    }
```

ソースコードを確認したところ、Keycloakがデフォルトで提供しているRequired Actionsのうち、以下は`SUPPORTED`を返していますのでApplication Initiated Actionsとして利用可能です。

* パスワード更新 (`UPDATE_PASSWORD`)
* プロフィール更新 (`UPDATE_PROFILE`)
* TOTP登録 (`CONFIGURE_TOTP`)
* MFA用WebAuthnセキュリティ・キー登録 (`keycloak-webauthn`)
* パスワードレス用WebAuthnセキュリティ・キー登録 (`keycloak-webauthn-passwordless`)

**自身で追加開発されたRequried Actionsの場合は、`initiatedActionSupport`を実装して`SUPPORTED`を返すように実装する必要がある点に注意してください**。未実装の場合はデフォルトで`NOT_SUPPORTED`扱いとなります。


# 再認証について

本機能でパスワードの変更アクションを実行させるといった場合は、再認証がどうなっているのか気になるところです。一般的に、セッションハイジャックなどで乗っ取られた場合を考慮して、パスワード変更のような重要情報の更新の前には再認証をさせたいためです。再認証についてはデザインドキュメントにも書かれていますが、Keycloak 8で部分的に実装されたとのこと。以下、引用です。

> Re-authentication was implemented in Keycloak 8, but only with a hardcoded re-authenticate after 300 seconds. More flexbility will be added here in the future as we introduce step-up and addaptive authentication in Keycloak.

認証時刻から300秒経った場合は再認証が求められる実装に今はハードコード実装されています。

#### 2021-03-22 追記
Keycloak 12からRequiredActionProviderインタフェースが拡張され、Required Actionの実装側で再認証に必要な時間を指定できるようになりました。

https://github.com/keycloak/keycloak/commit/8597edba8ef4ed62dccec6125386ce079a849e6c

デフォルト300秒は変わっていませんが、`getMaxAuthAge`の戻り値として`0`を返すようにすれば常に再認証必要となるようです。


# `kc_action`パラメータが改ざんされても大丈夫？

署名なしの認可リクエストのパラメータで`kc_action`を送っていますので、利用者自身でパラメータを書き換えて実行するアクションを変更することが容易にできてしまいます。また、悪意ある第三者がURLを作成して踏ませる、といったことも可能です。できることは元々許可されているアクションを実行できるだけはありますが、**例えばKeycloakの使い方として、プロフィール変更やパスワード変更はKeycloakで行わせていないという使い方の場合は要注意です**。デフォルトでプロフィール変更とパスワード更新のRequired Actionsは有効になっているため、**明示的にオフ**にしておかないと意図せずユーザがこれらのアクションを実行し、データ更新を行ってしまうケースが考えられます。


# まとめ

というわけで今回はApplication Initiated Actionsという比較的新しい機能を紹介しました。使わないという方も、ユーザー側からアクションを呼び出せてしまうというところがあるので、既存のRequired Actionsの有効/無効設定は一度見直したほうが良さそうです。