Keycloakのサービスアカウントを使用してユーザ情報一覧を取得する

  •  
 
ホス2023年7月6日 - 15:25 に投稿

タグ

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を取得する場合のシーケンスは以下のようになります。

sequence_code.png

サービスアカウントを使ったユーザ情報の取得

サービスアカウントを利用する場合は、OAuth2.0の「Client Credentials Grant」を利用することで、ユーザの操作を介することなくアクセストークンを取得します。

ユーザによる認証の代わりに、RPが事前に持っている Client ID と Client Secret を使って、Keycloakのトークンエンドポイントに認証を行い、トークンを取得します。

シーケンス図は以下のようになります。

sequence_client.png

サービスアカウントを使った場合、利用者による認証/認可の操作を行うことなくトークンの取得ができていることが分かります。
上記のシーケンス図では、最初にブラウザからのアクセスを記載しておりますが、あくまでも一例であり、他にはRPに実装されているcronやbatchなどがアクションを起こす場合などが考えられます。(RPに依ります)

認可コードフローでは、ユーザがアクションを起こしたときに認証が発生していましたが、サービスアカウントは、RPがアクションを起こすときに認証が発生します。

Code Flowでは、認証を行ったユーザのUserinfoしか取得できませんが、サービスアカウントではKeycloakの設定次第で、それ以外の情報を取得することができます。

サービスアカウントの設定方法

  1. サービスアカウントはClient毎に設定するため、事前にClientを作成します。
    1. Step1
      • CreateClient_1.png
    2. Step2
      • CreateClient_2.png
      • Client authenticationはONにしてください。
        • この設定をONにするとConfidential、OFFにするとPublicなクライアントとなります。
      • Authentication flow
        • Service accounts rolesをONにしてください。
          後からでもONにできます。
    3. Step3
      • CreateClient_3.png
      • Valid redirect URIsはこのあと検証する際に指定する必要があるため、設定しておいてください。
  2. すでに作成済みのClientを利用する場合は、「Capability config」にある以下の項目を変更してください。
    • Client authentication
      • ON
    • Service accounts roles
      • ON
    • ServiceAccountConfig_1.png
  3. サービスアカウントにロールを付与します。
    1. 「Service accounts roles」タブを開き、「Assign role」ボタンをクリックします。
      • serviceaccountrole_1.png
    2. 「Filter by realm roles」をクリックして、「Filter by clients」を選択します。
      • serviceaccountrole_2.png
    3. リストの中から、view-usersを選択して、「Assign」ボタンをクリックします。
      • ユーザの一覧を取得するには、view-usersロールが必要となります。
      • serviceaccountrole_3.png
    4. ロールがAssignされたことを確認します。
      • serviceaccountrole_4.png

サービスアカウントを利用するサンプルリクエスト (cURL)

  1. アクエストークンを取得します。

    • Basic認証を利用する場合

      1. Client ID と Client SecretをBase64でエンコードする (それぞれご自身の環境に読み替える)

        echo -n '<Client ID>:<Client Secret>' | base64
        
      2. リクエストを送信する

        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"
      
  2. ユーザ情報の一覧を取得します。

    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"
    
  3. 以下のように、ユーザ情報が取得できることが確認できます。

    [
      {
        "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があるかで判断ができます。
対処方法
  • サービスアカウントに、そのエンドポイント(今回の場合はusersエンドポイント)を利用するためのロールを付与してください。
  • 上記のロールを付与したあとに発行したアクセストークンを利用してください。

参考サイト

コメントを追加

プレーンテキスト

  • HTMLタグは利用できません。
  • 行と段落は自動的に折り返されます。
  • ウェブページのアドレスとメールアドレスは自動的にリンクに変換されます。
CAPTCHA
この質問はあなたが人間の訪問者であるかどうかをテストし、自動化されたスパム送信を防ぐためのものです。
画像
CodeFlow シーケンス図
Client シーケンス図
クライアント作成画面1
クライアント作成画面2
クライアント作成画面3
クライアント作成画面4
サービスアカウント有効化設定
サービスアカウントロール付与1
サービスアカウントロール付与2
サービスアカウントロール付与3
サービスアカウントロール付与4