KeycloakのRequired Actionsをカスタマイズしてみる

## Required Actions とは

Required Actions とは、ユーザに必須で実行させるアクションを設定する機能であり、
例えば、初回ログイン時にプロフィール情報を登録させたり、OTP 認証の設定を実施させるようなケースで利用できます。
本記事では、Required Actions を動かしつつ、一部処理のカスタマイズして適用するところまで検証してみます。

## Required Actions を試す

### アカウント作成

検証用アカウントとして、2 つのアカウントを作成します。

| ユーザー名 | 用途                                  |
| ---------- | ------------------------------------- |
| user1      | Required Actions を設定するユーザー   |
| user2      | Required Actions を設定しないユーザー |
|            |                                       |

### Required Actions の設定

#### 有効化

`Authentication` → `Required Actions` を選択すると利用可能な Required Actions の一覧が表示される。  
一覧の`Enabled`で Required Actions の有効/無効を切り替えることができる。設定したい Required Actions がある時は、まずここで有効化する必要がある。  
また、`Set as default action`というトグルスイッチがあり、こちらを有効にすると、新規でユーザーを作成した時に、デフォルトで対象の Required Actions がユーザーに適用される。

今回は、`Update Password`を利用するため、そちらが有効化されていることを確認する（デフォルト適用は OFF にする）

![①-加工.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/313c3229-7673-b6ff-3570-6e542e787430.png)


#### ユーザー適用

個別にユーザーへ適用する場合は、個々のユーザーの`detail`で設定する。以下のキャプチャのように`Required user actions`というセレクトリストがあり、ここで利用可能な Required Actions を選択することができる。  
今回は`Update Password`を利用したいため、`user1`で選択して、保存する。

![②-加工.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/3c984504-dd82-296c-c5b6-615c30b2fe4c.png)


### 動作検証

#### user1 で検証

user1 でアカウントコンソール画面へログインすると、認証完了後にパスワード変更画面が表示される。

![③.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/17565e82-956b-98fb-f0b0-f00833404ff3.png)
![④.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/9e671901-050d-9752-a259-212a26dbbbd0.png)


#### user2 で検証

user2 でアカウントコンソール画面へログインすると、認証完了後にパスワード変更画面が表示されず、直接アカウントコンソール画面へ遷移する。

![⑤.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/fd7b2284-0ac7-61f5-1ca2-3168e8b84c50.png)
![⑥.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/624549ca-6025-71aa-5755-12c2f07861c7.png)



ここまでで、Required Actions を設定すると、ユーザーに必須のアクションを適用できることが確認できた。

## RequiredAction のカスタマイズ

### カスタマイズ前

アカウントコンソールでは、ユーザー自身でパスワード変更することができる画面があり、今回はこちらの動きを変更する。

デフォルトでは、下記のキャプチャのようにパスワード変更すると、すぐにパスワード変更する画面が表示され、新しいパスワードを入力することで更新することができる。

![⑦.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/e55b1556-fc23-194d-da41-68340d47cbe9.png)
![⑧.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/c524ac2f-2d96-c3b2-9541-63cc6c27a1fb.png)


セキュリティ観点で、よく重要処理前に再度認証情報を要求するケースがあり、今回パスワード変更についても、必ず再認証を行うようにカスタマイズをしてみる。

### カスタマイズ

パスワード変更時、Keycloak のデフォルトでは 300 秒間は再認証不要となっており、最後の認証から 300 秒経過した場合は認証画面が表示されるようになっている。
今回はこちらを利用して都度認証画面が表示されるように改修する。

```java:UpdatePasswordRequiredAction.java
public class UpdatePasswordRequiredAction implements RequiredActionProvider {

  　・・・

    @Override
    public void evaluateTriggers(RequiredActionContext context){
        ・・・
    }

    @Override
    public void processAction(RequiredActionContext context) {
        ・・・
    }

    /**
     * 認証有効時間を返却
     * @note 常に再認証を実行させるため固定で0を返却(デフォルトは300)
     * @return 0
     */
    @Override
    public int  getMaxAuthAge() {
        return 0;
    }
}
```
上記が今回改修したコードでkeycloakの[UpdatePassword.java](https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java)を参考に`getMaxAuthAge()()`で常に0(都度認証)が返るように変更しています。
`getMaxAuthAge()`はデフォルトでは`MaxAuthAgePasswordPolicyProviderFactory.DEFAULT_MAX_AUTH_AGE`の値もしくは、パスワードポリシーで設定した期間が返るようになっています。

### SPI 適用
Required Actionも同様にSPIでKeycloakに反映することができます。
作成したコードを`mvn package`でJARを作成し、Keycloakの`providers`フォルダに配置するいつもの流れです。
フォルダ配置まで完了したらあとは動作確認を確認していきます。

#### Keycloak 起動
Keycloakを起動し、SPIが適用されると以下のようなコンソールログが出力されます。
WARNレベルのログですが、internalまたはprivateの依存関係を使用する場合に予告なく変更される可能性がある旨の警告のため、今回は特に気にする必要はありません。
バージョンアップなどの差異は注意が必要といった感じです。

```shell
> kc.bat start-dev
Updating the configuration and installing your custom providers, if any. Please wait.
2023-12-11 13:38:08,374 WARN  [org.keycloak.services] (build-7) KC-SERVICES0047: UPDATE_PASSWORD (org.keycloak.examples.requiredactions.UpdatePasswordRequiredActionFactory) is implementing the internal SPI required-action. This SPI is internal and may change without notice
```

### 動作検証

カスタマイズ前と同様にアカウントコンソールからパスワードを変更を操作してみます。

![⑨.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/40d43964-009f-8739-fe8c-854171d32199.png)
![⑩.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/c5c60149-07dd-60a3-c241-736eb88e4e3e.png)
![⑪.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333320/e1242591-d754-97fc-8fd4-f457ead3bcf4.png)



上記キャプチャの通り、前回は表示されなかった認証画面がパスワード変更画面の前に表示されることが確認できます。
念のため、すぐに同様のパスワード変更操作を実施すると、再度表示されるため、都度表示が実現できることが確認できる。

## まとめ
今回はパスワード変更のRequired Actionsを用いて簡単なカスタマイズを検証してみた。
基本的には`RequiredActionProvider`を継承したクラスを作成してSPIとして適用することで、任意の必須処理を追加できる。
`Required Actions`はKeycloak上に登録しておけば、Authenticatorから認証フローの途中で条件に応じて適用したりなど、便利に使える機能であるため、Keycloak標準には無い独自の要件などの追加の際は、カスタマイズ方法の一つとして覚えておきたい。

## 参考資料

- [Keycloak 公式ドキュメント](https://www.keycloak.org/docs/latest/server_development/)
- [Keycloak 日本語公式ドキュメント](https://keycloak-documentation.openstandia.jp/)
- [UpdatePassword のコード](https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java)