Skip to content

Keycloakのロールについて学ぶ

はじめに

Keycloakは、オープンソースのアイデンティティおよびアクセス管理ソリューションであり、OpenID Connect や SAML を利用した認証ができるツールです。Keycloakの機能の1つに、ロールベースのアクセス制御があります。ロールは、特定の権限やアクセスレベルを持つユーザーグループに割り当てられ、アプリケーションやサービスへのアクセスを制御します。
しかし、Keycloak の管理画面を見ると、ロールの設定方法が複雑で理解しにくいです。そこで、この記事では、Keycloakのロールについて理解を深めるために、ロールの概念や設定方法について学習した内容をアウトプットします。

CHANGELOG

DateChanges
2025-01-12Added
- レルムロールとクライアントロールの比較表を追加

Changed
- “ロールについて” の文章を更新
- 参考リンクを一部修正

成果物

https://github.com/kntks/blog-code/tree/main/2024/05/keycloak-role

環境

バージョン
MacVentura 13.2.1
Keycloak23.0.6
Docker26.0.0
Docker Composev2.24.5
Terraform1.8.2

環境構築

compose.yaml
services:
keycloak:
image: quay.io/keycloak/keycloak:23.0.6
ports:
- target: 8080
published: 8080
protocol: tcp
mode: host
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
command: ["start-dev"]

起動する

Terminal window
docker compose up

http://localhost:8080/auth にアクセスして、Keycloakの管理画面にログインします。デフォルトのユーザー名とパスワードは、admin です。

Terraform を使用するために Keycloak側のセットアップ が必要です。

terraform/terraform.tfvars
client_id = "terraform"
client_secret = "client secretをコピペ"
url = "http://localhost:8080"

terraform apply で Keycloak の設定を行います。

Terminal window
cd terraform
terraform init
terraform plan
terraform apply -auto-approve

ロールについて

Keycloakでは、ユーザーやグループにアクセス権限を付与するために「ロール」を利用します。ロールには大きく分けて「レルムロール」と「クライアントロール」の2種類があります。

レルムロール (Realm Roles)

  • 定義の範囲
    レルム全体に適用されるロールで、同じレルム内のすべてのクライアントで利用可能です。
  • 用途
    グローバルな権限や役割を定義する際に使用されます。たとえば、すべてのアプリケーションに共通する「admin」や「user」などのロールを作成できます。
    • admin: 全アプリケーションに対する管理者権限
    • user: 一般ユーザー権限
    • manager: チーム管理者権限

クライアントロール (Client Roles)

  • 定義の範囲
    特定のクライアント(アプリケーション)に限定されたロールで、それぞれのクライアントには専用の名前空間があります。
  • 用途
    クライアント固有の権限や役割を定義する際に使用されます。各クライアントごとに個別のロールセットを持ち、アクセス制御を細かく設定できます。

  • ソーシャルメディアアプリケーションにおけるロール:
    • post: 投稿権限
    • comment: コメント権限
    • like: いいね権限

レルムロールとクライアントロールの比較

特徴レルムロールクライアントロール
適用範囲レルム全体特定のクライアント
権限の共有複数のクライアント間で共有可能クライアントごとに個別
用途グローバル権限(例: 管理者、一般ユーザー)クライアント固有の権限(例: 投稿、コメント)
名前空間レルムレベルで一意の名前空間クライアントごとに専用の名前空間

割り当て対象

また、ロールを割り当てる対象は以下の3つがあります。

  • Group(以下:グループ)
    グループにつけるロールです。グループは、ユーザーの集合であり、ユーザーにロールを割り当てるための便利な方法です。グループにロールを割り当てることで、グループ内のすべてのユーザーにそのロールが自動的に割り当てられます。

  • User(以下:ユーザー)
    ユーザーとロールを定義します。マッピングを作成することでユーザーにレルムロールまたはクライアントロールを割り当てることでき、そのユーザーに特定のアクセス許可を付与できます。

  • Service account(以下:サービスアカウント)
    各クライアントには、アクセストークンを取得するためのサービスアカウントが組み込まれています。Client Credentials Grants を利用してアクセストークンを取得する際に使用します。

参考:

デフォルトで設定されているロール

Terraform でレルム(myrealm)、クライアント(myapp)、ユーザー(myuser)を作成しました。それぞれについているロールを確認してみましょう。

このセクションでは、アクセストークンと Keycloak の管理画面からロールを確認します。

管理画面からロールを確認する

レルムロール

レルムにデフォルトで設定されているロールを確認します。Realm settings -> User registration -> Default roles に表示されているロールが、レルムにデフォルトで設定されているロールです。 realm-roles-menu

クライアントロール

クライアントにデフォルトで設定されているロールを確認します。myapp のクライアントの詳細画面に移動し、Roles タブをクリックします。デフォルトでは、何も作成されていません。

default-client-roles

グループ

デフォルトでグループは作成されていないので、ロールは割り当てられていません。 default-group-roles

ユーザー

myuser をクリックして、ユーザーの詳細画面に移動します。 users-list

Role Mappings タブを開くと、ロールを確認できます。ユーザーに直接割り当てられているロール(default-role-myrealm)が表示されます。 default-user-role-mapping-hide-inherited-roles

Hide inherited roles のチェックを外すと、他に割り当てられている5つのロールが表示されます。
manage-account、manage-account-links、view-profile のロール名に account が付いていることから、account クライアントのロールであることがわかります。 default-user-role-mapping

default-role-myrealm 以外のロールは、Realm settings -> User registration -> Default roles にある設定によって自動的に割り当てられています。 default-realm-roles

Use default roles to automatically assign user role mappings when a user is created

(訳)デフォルトのロールを使用して、ユーザー作成時にユーザーのロールマッピングを自動的に割り当てます。

引用:Using default roles - Keycloak Documentation

サービスアカウント

クライアント詳細画面から Service accounts roles タブをクリックし、Hide inherited roles のチェックを外すと、サービスアカウントに割り当てられているロールが表示されます。 default-client-service-account-roles

アクセストークンのロールを確認する

先ほどは管理画面からロールの確認をしました。今度は、アクセストークンのロールを確認します。アクセストークンは、Resource Owner Password Credentials Grants (Direct Access Grants)とClient Credentials Grants を使用して取得します。それぞれの方法で access_token を取得します。

ユーザー

Resource Owner Password Credentials Grant でアクセストークンを取得する例

Terminal window
curl -s \
-d "client_id=myapp" \
-d "client_secret=xxxxxxx" \
-d "username=myuser" \
-d "password=myuser" \
-d "grant_type=password" \
-d "scope=openid" \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

その後、取得した access_token を jwt.io に貼り付け、Payload を確認します。
※ロールに関する情報のみ表示しています。

{
"aud": "account",
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
}

サービスアカウント

Client Credentials Grants でアクセストークンを取得する例

Terminal window
curl -s -X POST \
--header 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=myapp' \
-d 'client_secret=xxxxxxx' \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

その後、取得した access_token を jwt.io に貼り付け、Payload を確認します。
※ロールに関する情報のみ表示しています。

{
"aud": "account",
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "profile email",
}

クライアントスコープとロールスコープマッピング

クライアントスコープとは、Keycloak においてレルムレベルで設定され、クライアントにリンクできるエンティティです。

Keycloak には、Client scopes という名前がついているものが2つあり、画像左のナビゲーションペインにある Client scopes と Clients の詳細画面の Client scopes タブです。

紛らわしいのでこの記事では、左のナビゲーションペインにある Client scopes を、“クライアントスコープメニュー”、Clients 詳細画面の Client scopes タブは”クライアントスコープタブ”と呼ぶことにします。 client-scopes

クライアントスコープメニューから作成できるクライアントスコープは、レルム内にあるすべてのクライアントと共有でき、クライアントスコープタブから個別のクライアントにクライアントスコープをリンクできます。クライアントスコープは、クライアントにリンクされると、そのクライアントに対してアクセストークンのスコープとして使用できるようになります。

アクセストークンの”scope”を追加する

ここではクライアントスコープを作成し、クライアントにリンクすることで、アクセストークンの “scope” に追加してみます。

クライアントスコープを作成する

クライアントスコープでは、クライアント共有のクライアントスコープを作成できます。 client-scopes-create-client-scope

my-client-scope というクライアントスコープを作成します。 client-scopes-create-client-scope-2

クライアントとクライアントスコープを関連付ける

次にクライアントスコープタブに移動し、my-client-scopemyapp クライアントにリンクします。 client-scopes-tab-add-client-scope

今回は Optional で追加します。 client-scopes-tab-add-client-scope-optional

アクセストークンの”scope”を確認する

以下のコードを実行し、アクセストークンを取得します。

Terminal window
curl -s \
-d "client_id=myapp" \
-d "client_secret=xxxxxxx" \
-d "username=myuser" \
-d "password=myuser" \
-d "grant_type=password" \
-d "scope=openid" \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

その後、取得した access_token を jwt.io に貼り付け、Payload から “scope” を確認します。

{
"scope": "openid profile email",
}

次は scope に my-client-scope を追加してアクセストークンを取得します。

Terminal window
curl -s \
-d "client_id=myapp" \
-d "client_secret=xxxxxxx" \
-d "username=myuser" \
-d "password=myuser" \
-d "grant_type=password" \
-d "scope=openid" \
-d "scope=openid my-client-scope" \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

こちらも同様に取得した access_token を jwt.io に貼り付け、Payload から “scope” を確認します。
先ほど作成した my-client-scope が追加されていることが確認できます。

{
"scope": "openid profile my-client-scope email"
}

クライアントスコープをクライアントに関連付ける際に、Assigned typeOptional にしました。これを Default に変更した場合は、トークンエンドポイントへのリクエストに scope パラメータが “openid” のみでも、my-client-scope が自動的に追加されます。

アクセストークンに付与されるロール

このセクションでは、ロールスコープマッピングについて学習します。

ロールスコープマッピングは以下の引用からアクセストークンに付与されるロールに関係していることがわかります。

Role Scope Mappings limit the roles declared inside an access token. When a client requests a user authentication, the access token they receive contains only the role mappings that are explicitly specified for the client’s scope.

(訳)ロールスコープマッピングは、アクセストークン内で宣言されるロールを制限します。クライアントがユーザ認証を要求したとき、彼らが受け取るアクセストークンには、クライアントのスコープに対して明示的に指定されたロールマッピングのみが含まれます。

引用:Role scope mappings

続いての引用から、デフォルトではユーザーに割り当てられているすべてのロールがアクセストークンに含まれることがわかります。

By default, each client gets all the role mappings of the user.

(訳)デフォルトでは、各クライアントはユーザーのすべてのロールマッピングを取得します。

引用:Role scope mappings

アクセストークンからデフォルトのロールを削除する

先ほどの”アクセストークンのロールを確認する”のセクションではアクセストークンのロールを確認しました。

しかし、デフォルトではユーザーに割り当てられているすべてのロールがアクセストークンに含まれるため、アクセストークンのサイズが大きくなる可能性があります。実際、以下の Realm settings -> User registration -> Default roles にロールを追加すると自動的にアクセストークンに含まれます。 default-realm-roles

デフォルトのロールを削除しても良いのですが、クライアントスコープタブから Full Scope Allowed を Off にすることで、アクセストークンについていたデフォルトのロールを削除できます。

Clients -> myapp -> Client scopes -> myapp-dedicated -> Scope の順にページ移動します。 clients-myapp-client-scopes-tab

Full scope allowed をクリックして “Off” にします。 clients-myapp-client-scopes-tab-scope-full-scope-allowd

再度アクセストークンを取得します。

Terminal window
curl -s \
-d "client_id=myapp" \
-d "client_secret=xxxxxxx" \
-d "username=myuser" \
-d "password=myuser" \
-d "grant_type=password" \
-d "scope=openid" \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

Payload を確認すると、audresource_accessrealm_access が削除されていることが確認できます。

{
"aud": "account",
"realm_access": {
"roles": [
"default-roles-myrealm",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
}

myapp ユーザーの Role mapping を確認すると、デフォルトのロールがついていますが、アクセストークンには含まれていません。 default-user-role-mapping

カスタムロールをアクセストークンに追加する

先ほど Off にした Full Scope Allowed はそのままにしておきます。

ここでは、レルムロール、myapp のクライアントロール、account のクライアントロールをユーザーに割り当て、アクセストークンに含まれるロールを確認します。

myapp クライアントの詳細画面から Role タブに移動し my-client-role ロール(クライアントロール)を作成します。 clients-myapp-roles-create-role

その後 Users -> myuser -> Role mapping に移動し、Assign role ボタンを押します。 users-myuser-role-mapping-tab

myuser のロールに my-client-role と、manage-account を追加します。myapp クライアントのロールと account クライアントのロールを割り当てることで、アクセストークンに含まれるロールを確認してみます。 users-myuser-role-mapping-assign-roles

現在ユーザーに割り当てられているロールは以下の通りです。 users-myuser-role-mapping-assigned

いつものようにアクセストークンを発行してみます。

Terminal window
curl -s \
-d "client_id=myapp" \
-d "client_secret=xxxxxxx" \
-d "username=myuser" \
-d "password=myuser" \
-d "grant_type=password" \
-d "scope=openid" \
"http://localhost:8080/realms/myrealm/protocol/openid-connect/token" | jq

access_token の Payload を確認してみると、resource_access に myapp のクライアントロールである my-client-role のみ表示され、アクセストークンには、aud が存在しませんでした。

{
"resource_access": {
"myapp": {
"roles": [
"my-client-role"
]
}
},
"scope": "openid profile email"
}

Full Scope Allowed が Off の場合、(デフォルトで作成されている)roles クライアントスコープは、ユーザーのロールをアクセストークンに追加するために使用されます。(実際に Assign type を Default から Optional に変更すると my-client-role もアクセストークンから無くなります。) client-scopes-list

roles クライアントスコープの Mappers client-scopes-roles-mappers

以下は、roles クライアントスコープの説明です。

This scope has mappers, which are used to add the roles of the user to the access token and add audiences for clients that have at least one client role.

(訳)このスコープにはマッパーがあり、アクセストークンにユーザーのロールを追加し、少なくとも1つのクライアントロールを持つクライアントのオーディエンスを追加するために使用されます。

引用:Protocol - Keycloak Documentation

roles クライアントスコープのスコープには何も設定していませんが、どうやら、myuser が持っているロールの中から、client_id が myapp であるクライアントにリンクされているクライアントロールのみがアクセストークンに含まれるようです。(メモ:この挙動はドキュメントを探しても見つかりませんでした。)

参考:

まとめ

この記事では、Keycloak のロールについて学習しました。Keycloak には、レルムロール、クライアントロール、グループロールの3つのロールがあります。また、ロールを割り当てる対象は、ユーザー、グループ、サービスアカウントの3つがあります。ロールを割り当てる対象にロールを割り当てることで、アプリケーションやサービスへのアクセスを制御できます。さらにクライアントスコープを使用することで、アクセストークンのスコープを制御できます。

さいごに

Keyclokaのクライアントスコープやマッパー周りは複雑で難しいです。さらに Usability improvements for Client Scope configuration #19869 のディスカッションにもあるように将来的にUIが改善されたり、Dedicated scopes が廃止されるかもしれません。そのため、もし Keycloak 24 以上を使用している場合は最新のドキュメントを参照することをオススメします。

参考