Keycloak の User fedration をやめて Identity Provider だけを使うようにした話

# background
keycloakを長らく user federation の LDAP で運用してきました。[LDAP で 2FA をするのもできた](https://qiita.com/uturned0/items/8f45ce492ec59680f9f2) のですが、OIDC の利用が推奨されたので user federation をやめて IdP(Identity provider) を使うことにしました。

# issues
user federationをやめることにより、以下の問題が発生しました。

1. LDAP で import された keycloak users は LDAP に完全に依存している。
    - LDAPを消すと、ユーザも消える!
        - ユーザにアサインしていたroleが全部消える
2. IdP(Identity Providers) でログインしたユーザは全く新しい新規ユーザとなる
    - LDAPに紐づいていたユーザの権限は持たない
    - LDAP userが残っている場合は、IdP ユーザは作られず、既存のLDAP userにlinkされる
        - この場合は LDAP userの権限が引き継がれるが、LDAPを消すとユーザごと消えるので、linkされたのも意味がなくなる
3. LDAPを使うと全ユーザのimportが一瞬でできますが、IdPの場合はユーザリストをとってこれないのでユーザにログインを試行してもらわないとユーザが作成されない。
    - ユーザがログインを試す前にroleをアサインしておくことができない

# solution
netの海を探しても全く事例が見つからないので、既存ユーザを活かすのは諦めました。
新規ユーザを無理やり作り、既存の権限を同じように付与しました。

# Procedure
以下の手順で移行を行います。

1. 既存(LDAP) usersの権限を全てメモっておく。
    1. realm admin, client roles, groups などを全てメモっておく。たくさんありすぎる場合はそれ用のscriptをchatGPTに書いてもらいましょう。
1. 移行scriptを用意しておく
    1. "1." でメモったuser権限を入れておきます
1. `import_users.py`
    1. 既存(LDAP)のkeycloak usersを出力する
    1. keycloak UI:
        1. User federationのLDAPをdisabledにする。
        1. `Remove imported` を実行し、既存のuserを全て消す
            1. ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/d399a180-3467-8afc-79b4-350277aad0a6.png)

    1. "3-1." で出力しておいた `users.json` を使って、LDAPに依存しない keycloak usersを一括で作成する。
        1. この時、userに対して IdP userのlinkを強制的に埋め込みます。これをしないと、IdP を使って login しようとしたユーザは「どのkeycloak userにlinkしますか？」と聞かれますが、そもそもkeycloak userにpasswordが設定されていないため、linkする対象として承認されることができず詰みます。
1. 移行scriptを実行し、LDAPユーザが持っていた権限を付与する



# 移行scripts

ここに使ったscriptを置いておきました。ほぼchatGPTに書いてもらったので超適当です。何も言わないで作ったらnode.jsとpythonで書かれましたw fileごとにpassword必要だし、全く正規化されてないです。

https://github.com/kujiy/keycloak-user-federation-to-identity-providers/tree/master

なお、業務に関連しそうなところは目視で削除してるのでこのrepo自体の動作確認はしてません。悪しからず。

適当すぎるポイントは、userを export → 削除 -> import する必要があるんですが、exportした後の削除はUIから行なってるので、IDEのdebug 機能でここでbreakpointして、UIから削除を実行した後に F9 で再開して import させてました。ひどいw

```py
        # ここで一時停止して、LDAPユーザをUIから全て消す
        pass
```

LDAP usersを消す方法

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/20ea1150-bad1-3c87-92e2-3c2de53218a8.png)





# IdP はまりポイント 1

IdPの設定で OIDC から user attributes が取れない問題で死ぬほどはまりました。IdPへのリクエスト scopeがデフォルトで `openid` しかないのが原因でした。advancedの中に隠れてます。

> Identity providers > Provider details > Settings
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/03c54e2c-1892-da65-43e6-2236f2038b2d.png)



# IdP はまりポイント 2
新しく作ったkeycloak userに `Identity provider links` をつけるのが難しくはまりました。

`Users > User details > Identity provider links` には２つの設定項目があります。これは想像ですが、私の環境ではこのようになりました。

- `User ID` は OIDC が返却する user profile の `username` に相当?
- `Username` は OIDC が返却する user prorileの `email` に相当

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/4fb353bd-4c50-b882-a111-894dae734657.png)

しかし、これは利用している OIDC(IdP) の user profileに大きく依存すると思うので適宜適切な値にしてください。

*IdPのmapperで username を違う attributeにアサインすることができますが、これは user の IdP linkには影響しないようです。

`Identity providers > Provider details > mappers` で usernameを email とは違うattributeにしてるけど、userのIdP Link に指定する username は emailだったの図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/71206915-0525-cdbb-1d15-3fafa9aeda4f.png)



keycloakの具体的な運用方法はまだまだnetの知見が少ないように思います。少しでも役立つ方がいましたらぜひいいねとストックをお願いします！