概要
- Keycloakには「Admin REST API」というREST APIが存在する
- ただしアクセスするためには以下の2段階のフローが必要で使うのがやや面倒くさい仕様である
- → この辺を隠蔽してくれるサードパーティライブラリがあったらうれしいと思い、アクティブに開発されていてStar数も700ある
Python Keycloakというライブラリを試してみた。
検証
まずはPython Keycloakをインストールする
requirements.txt
python-keycloak==5.1.2
pip install -r requirements.txt
READMEの通りにようにKeycloakAdminのインスタンスを作成する。
KeycloakAdminに渡すKeycloakOpenIDConnectionに必要な値は以下
- server_url: KeycloakのベースURL
- username: Admin権限を持つKeycloakのユーザーのユーザー名
- password: Admin権限を持つKeycloakのユーザーのパスワード
- realm_name: レルム名
- client_id: Keycloakで払い出したクライアントID
- client_secret_key: Keycloakで払い出したクライアントシークレット
main.py
from keycloak import KeycloakOpenIDConnection, KeycloakAdmin keycloak_connection = KeycloakOpenIDConnection( server_url="http://localhost/", username='admin', password='password', realm_name="master", client_id="testclient", client_secret_key="***", verify=True) keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
これでAdmin REST APIを叩く準備ができている。
続けて例えば以下のコードを追加して実行する。
# ...省略 print(keycloak_admin.get_user(user_id="7b898848-958f-4534-8459-764e916d72d5"))
すると以下のようにユーザー情報を取得できる。
$ python main.py { "id":"7b898848-958f-4534-8459-764e916d72d5", "username":"admin", "email":"test@example.com", "emailVerified":true, "createdTimestamp":1738078594021, "enabled":true, "totp":false, "disableableCredentialTypes":[ ], "requiredActions":[ ], "notBefore":0, "access":{ "manageGroupMembership":true, "view":true, "mapRoles":true, "impersonate":true, "manage":true } }
アクセストークンの取得タイミング
先にも書いたが、KeycloakのAdmin REST APIは使う前に必ずアクセストークンを発行する必要がある。
従って、Python Keycloakはいつアクセストークンを発行しているのかが気になった。Keycloakの前段にリバースプロキシを置いて確認したところ、以下のタイミングではアクセスログが出力されなかった(=アクセストークンをまだ発行していない)
keycloak_admin.get_userを実行したときに以下の2つのアクセスログが出力された。1つ目がアクセストークンを発行するAPIへのリクエストである。
POST /realms/master/protocol/openid-connect/tokenGET /admin/realms/master/users/7b898848-958f-4534-8459-764e916d72d5?...(省略)
ゆえに、アクセストークンの事前取得 & 永続化はしておらず、Admin REST APIを叩くメソッドを呼ぶたびに、都度アクセストークンを取得しにいく実装っぽいことが分かった。
実際のログ
proxy-1 | {"level":"info","ts":1738078832.4040802,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"192.168.97.1","remote_port":"58394","client_ip":"192.168.97.1","proto":"HTTP/1.1","method":"POST","host":"localhost","uri":"/realms/master/protocol/openid-connect/token","headers":{"Content-Type":["application/x-www-form-urlencoded"],"Content-Length":["153"],"User-Agent":["python-requests/2.32.3"],"Accept-Encoding":["gzip, deflate"],"Accept":["*/*"],"Connection":["keep-alive"]}},"bytes_read":153,"user_id":"","duration":0.093971635,"size":3551,"status":200,"resp_headers":{"X-Xss-Protection":["1; mode=block"],"Cache-Control":["no-store"],"Referrer-Policy":["no-referrer"],"X-Frame-Options":["SAMEORIGIN"],"Server":["Caddy"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains"],"Pragma":["no-cache"],"Content-Length":["3551"],"X-Content-Type-Options":["nosniff"],"Content-Type":["application/json"]}}
proxy-1 | {"level":"info","ts":1738078832.410743,"logger":"http.log.access.log0","msg":"handled request","request":{"remote_ip":"192.168.97.1","remote_port":"58396","client_ip":"192.168.97.1","proto":"HTTP/1.1","method":"GET","host":"localhost","uri":"/admin/realms/master/users/7b898848-958f-4534-8459-764e916d72d5?userProfileMetadata=False","headers":{"Content-Type":["application/json"],"Authorization":["REDACTED"],"User-Agent":["python-requests/2.32.3"],"Accept-Encoding":["gzip, deflate"],"Accept":["*/*"],"Connection":["keep-alive"]}},"bytes_read":0,"user_id":"","duration":0.003856672,"size":341,"status":200,"resp_headers":{"X-Frame-Options":["SAMEORIGIN"],"Content-Type":["application/json;charset=UTF-8"],"Server":["Caddy"],"Strict-Transport-Security":["max-age=31536000; includeSubDomains"],"X-Xss-Protection":["1; mode=block"],"Content-Length":["341"],"Cache-Control":["no-cache"],"Referrer-Policy":["no-referrer"],"X-Content-Type-Options":["nosniff"]}}
Admin REST APIとKeycloakAdminのメソッド名の対応
実際には
という使い方になりそうである。
urls_patterns.pyというファイルにAdmin REST APIのURLが定数で定義されている。従って、まずは使いたいAPIのURLと対応する定数をここから探すことになりそうである。
https://github.com/marcospereirampj/python-keycloak/blob/master/src/keycloak/urls_patterns.py
定数が分かったら、keycloak_admin.pyを定数でgrepすると使うべきメソッド名に辿り着けそうである。
https://github.com/marcospereirampj/python-keycloak/blob/master/src/keycloak/keycloak_admin.py