【Keycloak】アクセストークンの Audience を変更する
Keycloak のアクセストークンにある aud クレームがデフォルトで account になっています。この aud が何者か気になったので、調べてみました。
その結果、アクセストークンの aud を変更する方法がわかりましたので、まとめます。
https://github.com/kntks/blog-code/tree/main/2024/09/keycloak-token-audience
| バージョン | |
|---|---|
| Mac | Sonoma 14.5 |
| Keycloak | 25.0.6 |
| Docker | 26.0.0 |
| Docker Compose | v2.24.5 |
| Terraform | 1.9.5 |
Keycloak のセットアップ
Section titled “Keycloak のセットアップ”compose.yaml ファイルを作成し、Keycloak を定義します。
services: keycloak: image: quay.io/keycloak/keycloak:25.0.6 container_name: keycloak ports: - target: 8080 published: 8080 protocol: tcp mode: host environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin command: ["start-dev"] volumes: - ./keycloak:/opt/keycloak/datadocker compose コマンドを実行して Keycloak を起動します。
docker compose upTerraformを実行する
Section titled “Terraformを実行する”Terraform を使用するために Keycloak に terraform クライアント を作成します。
Keycloak で terraform クライアントを作成したら、クライアントシークレットをコピーし、以下ように、client_id, client_secret, url を設定します。
client_id = "terraform"client_secret = "xxxxxxxxxxxxxxxxx"url = "http://localhost:8080"terraform apply で Keycloak の設定を行います。
cd terraformterraform initterraform planterraform apply -auto-approveAudience とは
Section titled “Audience とは”アクセストークンの Audience
Section titled “アクセストークンの Audience”Audience(オーディエンス)は、JWT(JSON Web Token)のクレームの1つで、JWT が意図されている受取人を識別します。
The “aud” (audience) claim identifies the recipients that the JWT is intended for.
「aud」(オーディエンス)クレームは、JWTが意図されている受取人を識別します。
ID トークンの Audience
Section titled “ID トークンの Audience”ID トークンの aud は、OAuth 2.0 クライアント ID である必要があります。
REQUIRED. Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value.
(訳)必須。このIDトークンが意図されているオーディエンス。オーディエンスの値として、必ずRelying Party(依存するサービス)のOAuth 2.0 client_idを含める必要があります。
引用:ID Token - OpenID Connect Core 1.0 incorporating errata set 2
Audience の確認方法
Section titled “Audience の確認方法”Audience の設定方法に進む前に、トークンの発行とデコード方法を紹介します。
方法は2つあります。
- Direct access grants を使用してトークンを取得する
- Evaluate を使用する
Direct access grants を使用してトークンを取得する
Section titled “Direct access grants を使用してトークンを取得する”myapp クライアントの Settings タブを開き、Direct access grants を確認します。
チェックが入っていない場合は、チェックを入れて保存します。

Credentials タブからクライアントシークレットをコピーしてください。

先ほどコピーしたクライアントシークレットを以下の curl コマンドに設定し、実行すると、アクセストークンと ID トークンを取得できます。
curl -s \ -d "client_id=myapp" \ -d "client_secret=xxxxxxxxxx" \ -d "username=myuser" \ -d "password=myuser" \ -d "grant_type=password" \ -d "scope=openid" \"http://localhost:8080/realms/myrealm/protocol/openid-connect/token"今回は以下のスクリプトを実行することで、アクセストークンと ID トークンをデコードします。
#!/bin/bash
# ref: https://gist.github.com/angelo-v/e0208a18d455e2e6ea3c40ad637aac53?permalink_comment_id=3920605#gistcomment-3920605
res=$(curl -s \ -d "client_id=myapp" \ -d "client_secret=QlaUgKTjZBgHr1vTM8K3clcsOb4IAxdc" \ -d "username=myuser" \ -d "password=myuser" \ -d "grant_type=password" \ -d "scope=openid test-client-scope" \"http://localhost:8080/realms/myrealm/protocol/openid-connect/token")
access_token=$(jq -r '.access_token' <<< $res)id_token=$(jq -r '.id_token' <<< $res)
echo "decode access_token"jq -R 'split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson' <<< $access_token
echo "decode id_token"jq -R 'split(".") | select(length > 0) | .[0],.[1] | @base64d | fromjson' <<< $id_tokenこのスクリプトを実行すると、以下のような結果が得られます。
$ bash decode.shアクセストークン
aud が account になっていることがわかります。さらに、realm_access や resource_access にデフォルトロールが含まれていることも確認できます。
{ "alg": "RS256", "typ": "JWT", "kid": "pwQ16gsNlgXi_D9Y1eK9oIhDOlAyMw9h00WG0ouKoKo"}{ "exp": 1726991696, "iat": 1726991396, "jti": "690709a1-d72e-464f-b900-e8a60bdd68d1", "iss": "http://localhost:8080/realms/myrealm", "aud": "account", "sub": "c1189b01-77cd-4c18-8b9b-f4647b99c9d8", "typ": "Bearer", "azp": "myapp", "sid": "10531aec-20ff-4eba-8a6e-6454b22a7aa4", "acr": "1", "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", "email_verified": false, "name": "foo bar", "preferred_username": "myuser", "given_name": "foo", "family_name": "bar", "email": "myuser@exmple.com"}ID トークン
aud はクライアント ID である myapp になっていることがわかります。
{ "alg": "RS256", "typ": "JWT", "kid": "pwQ16gsNlgXi_D9Y1eK9oIhDOlAyMw9h00WG0ouKoKo"}{ "exp": 1726991696, "iat": 1726991396, "jti": "e6d2dddc-815b-47d1-a733-8cda8c3dd238", "iss": "http://localhost:8080/realms/myrealm", "aud": "myapp", "sub": "c1189b01-77cd-4c18-8b9b-f4647b99c9d8", "typ": "ID", "azp": "myapp", "sid": "10531aec-20ff-4eba-8a6e-6454b22a7aa4", "at_hash": "8uGfup0t4q2e_W8H7_cKmA", "acr": "1", "email_verified": false, "name": "foo bar", "preferred_username": "myuser", "given_name": "foo", "family_name": "bar", "email": "myuser@exmple.com"}Evaluate を使用する
Section titled “Evaluate を使用する”Client scopes タブを開き、Evaluate をクリックすると、クライアントスコープを評価でき、実施にどのようなクレームが含まれるかを確認できます。

Keycloak のクライアント設定
Section titled “Keycloak のクライアント設定”Full scope allowed を OFF にする
Section titled “Full scope allowed を OFF にする”先ほど access_token のデコード結果から使わないデフォルトのロールがありました。
Keycloak はデフォルトで Full scope allowed が ON になっているため、クライアントに設定されたデフォルトロールがアクセストークンに含まれます。
そのため、Full scope allowed を OFF にすることで、デフォルトのロールを取り除きます。
myapp のクライアントページに移動し、Client scopes タブを開くと、myapp-dedicated があるのでクリックします。

Scope タブに切り替えて Full scope allowed を OFF にして保存します。

再度、decode.sh を実行すると access_token から aud や realm_access、resource_access が無くなったことを確認できます。
{ "alg": "RS256", "typ": "JWT", "kid": "pwQ16gsNlgXi_D9Y1eK9oIhDOlAyMw9h00WG0ouKoKo"}{ "exp": 1727142673, "iat": 1727142373, "jti": "f0429ab4-f22f-4ce5-aeaf-f2d273c4e885", "iss": "http://localhost:8080/realms/myrealm", "sub": "c1189b01-77cd-4c18-8b9b-f4647b99c9d8", "typ": "Bearer", "azp": "myapp", "sid": "d0b2aea8-cbfb-4e06-b969-962f9c99c490", "acr": "1", "scope": "openid profile email", "email_verified": false, "name": "foo bar", "preferred_username": "myuser", "given_name": "foo", "family_name": "bar", "email": "myuser@exmple.com"}Audiance の設定方法
Section titled “Audiance の設定方法”ここではアクセストークンの aud を変更する方法を説明します。
audience に値をセットする方法は2つあります。
参考:Audience support - Keycloak Documentation
クライアントロールを使用した自動追加
Section titled “クライアントロールを使用した自動追加”Client scopes ページに roles という名前のクライアントロールがデフォルトで存在します。

roles をクリックし、 Mappers タブを開くと、audience resolve が設定されています。

このマッパーは、自分自身(クライアント)とは別クライアントのロールを持っている場合、別クライアント ID を自動的に aud に追加します。
An Audience Resolve protocol mapper is defined in the default client scope roles.
Audience Resolveプロトコルマッパーは、デフォルトのクライアントスコープ「roles」に定義されています。The mapper checks for clients that have at least one client role available for the current token.
このマッパーは、現在のトークンに対して少なくとも1つのクライアントロールが利用可能なクライアントをチェックします。The client ID of each client is then added as an audience, which is useful if your service clients rely on client roles.
そして、各クライアントのクライアントIDがオーディエンスとして追加されます。これは、サービスクライアントがクライアントロールに依存している場合に役立ちます。
aud にクライアント ID が自動で追加されることを確認する
Section titled “aud にクライアント ID が自動で追加されることを確認する”実際に、myapp クライアントに realm-management クライアントのロールを追加して、aud に realm-management クライアント ID が追加されることを確認します。
今回は、realm-management クライアントにあるロール view-client を myapp クライアントに追加します。

myapp クライアントの Client scopes タブを開き、myapp-dedicated クライアントスコープをクリックします。
※ 新規にクライアントスコープを作成しても構いません。

Scope タブに切り替えて、view-client を追加して保存します。

アクセストークンにロールを反映するためには、ユーザーにもロールを割り当てる必要があります。

これで、myapp クライアントと myuser に view-client ロールが割り当てられました。アクセストークンを確認してみましょう。
意図した通り、view-client は realm-management クライアントのロールなので、aud に realm-management クライアント ID が自動で追加されることを確認できました。
{ "alg": "RS256", "typ": "JWT", "kid": "pwQ16gsNlgXi_D9Y1eK9oIhDOlAyMw9h00WG0ouKoKo"}{ "exp": 1727186929, "iat": 1727186629, "jti": "d1fcb2b0-f278-4b4d-b320-ed705f476d16", "iss": "http://localhost:8080/realms/myrealm", "aud": "realm-management", "sub": "c1189b01-77cd-4c18-8b9b-f4647b99c9d8", "typ": "Bearer", "azp": "myapp", "sid": "d69f1f0d-a1cb-48a9-8341-983c48443153", "acr": "1", "resource_access": { "realm-management": { "roles": [ "view-clients", "query-clients" ] } }, "scope": "openid profile email", "email_verified": false, "name": "foo bar", "preferred_username": "myuser", "given_name": "foo", "family_name": "bar", "email": "myuser@exmple.com"}先ほど、クライアントとユーザーに紐付けた view-client ロールは外します。
ハードコード
Section titled “ハードコード”クライアントロールを使用した aud の自動追加では、自分自身のクライアント ID をアクセストークンの aud に追加することはできません。
そのためもし、自分自身のクライアント ID をアクセストークンの aud に追加したい場合は、ハードコードする必要があります。
そのほかにも、URLなどのカスタム値を設定するときに使用できます。
aud にクライアント ID が追加されることを確認する
Section titled “aud にクライアント ID が追加されることを確認する”今度は、アクセストークンの aud を自分自身のクライアント ID(myapp)に変更します。
再度、myapp クライアントの Client scopes タブを開き、myapp-dedicated クライアントスコープをクリックします。

Mappers タブから Configure a new mapper をクリックしてください。

Audience を選択します。

Include Client Audience にクライアント ID である myapp を入力して保存します。

設定が完了しました。アクセストークンを確認してみましょう。
アクセストークンをデコードした結果、aud に myapp クライアント ID が追加されることを確認できました。
{ "alg": "RS256", "typ": "JWT", "kid": "pwQ16gsNlgXi_D9Y1eK9oIhDOlAyMw9h00WG0ouKoKo"}{ "exp": 1727190112, "iat": 1727189812, "jti": "bbe17c2c-4ad5-4b2d-9435-70c77ea7048b", "iss": "http://localhost:8080/realms/myrealm", "aud": "myapp", "sub": "c1189b01-77cd-4c18-8b9b-f4647b99c9d8", "typ": "Bearer", "azp": "myapp", "sid": "0bb0aa25-82d4-4257-918b-ead49129fc1f", "acr": "1", "scope": "openid profile email", "email_verified": false, "name": "foo bar", "preferred_username": "myuser", "given_name": "foo", "family_name": "bar", "email": "myuser@exmple.com"}- Audience は JWT のクレームの1つです、
- アクセストークンの
audは JWT が意図されている受取人です。 - ID トークンの
audは OAuth 2.0 クライアント ID である必要があります。 - Keycloak でアクセストークンの
audを変更する方法は2つあります。- クライアントロールを使用した自動追加
- ハードコード