Keycloakとは
Keycloakとは、シングルサインオン(SSO)を実現するソフトウェアです。SAMLやOpenID Connectといった標準プロトコルに対応しています。 また、IAM(Identity and Access Management)の機能も持っており、ユーザの名前やメールアドレスなどのユーザ情報を管理できます。
ユーザ情報は、OpenID Connectの仕様に則って利用する場合、Userinfoエンドポイントを通じてRelaying Party(RP)に提供されます。
一般的なOpenID Connectによるユーザ情報の取得
サービスアカウントの話をする前に、まずは一般的なOpenID Connectによるユーザ情報の取得について話します。
OpenID Connectでシングルサインオンを実現する際に最もよく使われる認可コードフローでは、 KeycloakをIdPとした場合、KeycloakにログインしたユーザがRPへ認可を与えるとアクセストークンを取得できます。
RPは、取得したアクセストークンをKeycloakのUserinfoエンドポイントに送信することで、引き換えにKeycloakからログインユーザの情報を取得することができます。
認可コードフローを使って、Userinfoを取得する場合のシーケンスは以下のようになります。
サービスアカウントを使ったユーザ情報の取得
サービスアカウントを利用する場合は、OAuth2.0の「Client Credentials Grant」を利用することで、ユーザの操作を介することなくアクセストークンを取得します。
ユーザによる認証の代わりに、RPが事前に持っている Client ID と Client Secret を使って、Keycloakのトークンエンドポイントに認証を行い、トークンを取得します。
シーケンス図は以下のようになります。
サービスアカウントを使った場合、利用者による認証/認可の操作を行うことなくトークンの取得ができていることが分かります。
上記のシーケンス図では、最初にブラウザからのアクセスを記載しておりますが、あくまでも一例であり、他にはRPに実装されているcronやbatchなどがアクションを起こす場合などが考えられます。(RPに依ります)
認可コードフローでは、ユーザがアクションを起こしたときに認証が発生していましたが、サービスアカウントは、RPがアクションを起こすときに認証が発生します。
Code Flowでは、認証を行ったユーザのUserinfoしか取得できませんが、サービスアカウントではKeycloakの設定次第で、それ以外の情報を取得することができます。
サービスアカウントの設定方法
- サービスアカウントはClient毎に設定するため、事前にClientを作成します。
- Step1
- Step2
Client authentication
はONにしてください。- この設定をONにすると
Confidential
、OFFにするとPublic
なクライアントとなります。
- この設定をONにすると
Authentication flow
Service accounts roles
をONにしてください。
後からでもONにできます。
- Step3
Valid redirect URIs
はこのあと検証する際に指定する必要があるため、設定しておいてください。
- Step1
- すでに作成済みのClientを利用する場合は、「Capability config」にある以下の項目を変更してください。
Client authentication
ON
Service accounts roles
ON
- サービスアカウントにロールを付与します。
- 「Service accounts roles」タブを開き、「Assign role」ボタンをクリックします。
- 「Filter by realm roles」をクリックして、「Filter by clients」を選択します。
- リストの中から、
view-users
を選択して、「Assign」ボタンをクリックします。- ユーザの一覧を取得するには、
view-users
ロールが必要となります。
- ユーザの一覧を取得するには、
- ロールがAssignされたことを確認します。
- 「Service accounts roles」タブを開き、「Assign role」ボタンをクリックします。
サービスアカウントを利用するサンプルリクエスト (cURL)
アクエストークンを取得します。
Basic認証を利用する場合
Client ID と Client SecretをBase64でエンコードする (それぞれご自身の環境に読み替える)
echo -n '<Client ID>:<Client Secret>' | base64
リクエストを送信する
curl -X POST \ -H "Authorization: Basic dGVzdF9jbGllbnQ6UTVuYUo2TVJTdk5aTFJUYmdzWHVZU0liT0VPVlN5M2c=" \ -d "grant_type=client_credentials" \ "http://localhost:8090/realms/test_realm/protocol/openid-connect/token"
client_idとclient_secretをボディに指定する場合
curl -X POST \ -d "client_id=test_client" \ -d "client_secret=Q5naJ6MRSvNZLRTbgsXuYSIbOEOVSy3g" \ -d "grant_type=client_credentials" \ "http://localhost:8090/realms/test_realm/protocol/openid-connect/token"
ユーザ情報の一覧を取得します。
curl -X GET \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJheXpubk16aEpXSkcyUmIwaThqekZFNmlwYVEtM1VyN2ZGY3BRT2RDc3ZZIn0.eyJleHAiOjE2ODg2MTkzMTgsImlhdCI6MTY4ODYxOTAxOCwianRpIjoiOGZkZTU1ODktNjQyZi00OWFjLWFiYzQtM2VlMmZjMzYxYzViIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDkwL3JlYWxtcy90ZXN0X3JlYWxtIiwiYXVkIjpbInJlYWxtLW1hbmFnZW1lbnQiLCJhY2NvdW50Il0sInN1YiI6IjQyMTA2MGQ5LWNlN2EtNDUzMi05NTVjLWY3ZDI4ZmYyNGQwZSIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3RfY2xpZW50IiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vcnAuZXhhbXBsZS5jb20iXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy10ZXN0X3JlYWxtIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJyZWFsbS1tYW5hZ2VtZW50Ijp7InJvbGVzIjpbInZpZXctdXNlcnMiLCJxdWVyeS1ncm91cHMiLCJxdWVyeS11c2VycyJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4zMS4wLjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10ZXN0X2NsaWVudCIsImNsaWVudEFkZHJlc3MiOiIxNzIuMzEuMC4xIiwiY2xpZW50X2lkIjoidGVzdF9jbGllbnQifQ.a-oA3pq3w3b_gtEoFR-sG4xUcfWzMhJ9HzwH_5hYdZkVtqO9IKvBd_Gsr_Dr1no2j40qdjOmto_D5GgmxE11sxDyZEcVYkzqMKPmuI6t3h2lFBIKhLNTK00QjQFFuaBMG1hUa8n15pobDCms1baK6E8OHPH_5FFFAKUzjzBCe2AYiBUpdDgdiEaa271cN_gjDbZzYC28EXpWbe0GHf6uksf4vQx2EFbKJLhuznB3frP4z9OYd9ao3tXNuhaSxhdGGDztpAlYomiDmRO23gKuhNO_fGEDMkOGnYpbFE2FGknKczNCs3Q6eR1LwaIpBWhcTu7lnUIKV88b47znAXsrWQ" \ -H "Content-Type: application/json" \ "http://localhost:8090/admin/realms/test_realm/users"
以下のように、ユーザ情報が取得できることが確認できます。
[ { "id": "03e25385-4bc5-4c66-a48c-e7366af5e3f0", "createdTimestamp": 1688550209999, "username": "tanaka", "enabled": true, "totp": false, "emailVerified": false, "firstName": "Jiro", "lastName": "Tanaka", "email": "tanaka@example.com", "disableableCredentialTypes": [], "requiredActions": [], "notBefore": 0, "access": { "manageGroupMembership": false, "view": true, "mapRoles": false, "impersonate": false, "manage": false } }, { "id": "4c8ea9b2-a08d-4622-b032-b10f7a3d037e", "createdTimestamp": 1688550175833, "username": "yamada", "enabled": true, "totp": false, "emailVerified": false, "firstName": "Taro", "lastName": "Yamada", "email": "yamada@example.com", "disableableCredentialTypes": [], "requiredActions": [], "notBefore": 0, "access": { "manageGroupMembership": false, "view": true, "mapRoles": false, "impersonate": false, "manage": false } } ]
トラブルシューティング
Userを取得しようとしたらunknown_errorが発生する
返却されるエラーのサンプル
json {"error":"unknown_error"}
考えられる原因
- サービスアカウントがそのエンドポイントを利用するためのロールを持っていない可能性があります。
- ロールを付与する前に発行したアクセストークンを利用している可能性があります。
- アクセストークンのPayloadをBase64デコードして、
resource_access
の中にrealm-management
に関するroleがあるかで判断ができます。
- アクセストークンのPayloadをBase64デコードして、
対処方法
- サービスアカウントに、そのエンドポイント(今回の場合はusersエンドポイント)を利用するためのロールを付与してください。
- 上記のロールを付与したあとに発行したアクセストークンを利用してください。
参考サイト
.node__content ol { list-style-type: auto; } .hljs-ln-n { display: none; } .page-node-type-blog-post p { color: #60696d; font-size: initial; line-height: 1.6em; margin: auto; } .anchor { padding-top: 6em; margin-top: -6em; } .node__content p, .node__content li { margin-bottom: 0.5em; }- 閲覧数 4051











コメントを追加