Keycloak 26.7.1 Evaluate 的 Access Token aud 狀態

Keycloak Evaluate 的 audience 驗證行為與實際 token 的 aud 組成

[chiehting]

keycloakoauth2.0oidc

783 Words CHANGE ME READ TIME3 Minutes, 33 Seconds

2026-08-11 08:00 +0800


Keycloak 發生錯誤

想要取得用戶透過 OIDC 登入時的 ID Token 跟 Access Token 的資料結構,使用工具 Keycloak 26.7.1 > Clients > Client scopes -> Evaluate 進行模擬。帶入下面參數進行模擬,會發生錯誤 Danger alert:Requested audience not available: grafana_dlqlr63acgdf

  • Realm:master
  • Scope parameter: openid
  • Users: justin.lee
  • Target audience: grafana_dlqlr63acgdf
  • grafana_dlqlr63acgdf client:未配置任何 Audience Mapper。

Keycloak log 分析

  • Evaluate 傳入 audience=grafana_dlqlr63acgdf 時,Keycloak 在 validateAudience 階段拋出 NotFoundException,token 產生流程中止(未產生 token)。
  • 透過 Grafana 的 log 紀錄 resource_access = { grafana_dlqlr63acgdf, account },而 aud = [ account ],差集恰為發起請求的 client 自身,可以判斷 Keycloak 的 audience 解析僅涵蓋「使用者持有角色的其他 client」,不含發起請求的 client 自身(該身分由 azp 承載)。因此以自身 client id 作為 target audience 時查無此 audience。
  • 待驗證:ClientScopeEvaluateResource.java:416 的實作,或 Keycloak 官方文件對 Evaluate 的 audience 參數說明。
2026-08-11 07:46:20,382 DEBUG [io.quarkus.vertx.http.runtime.ForwardedParser] (vert.x-eventloop-thread-1) Recalculated absoluteURI to https://keycloak.example.com/admin/realms/master/clients/53b42e27-3030-4e16-8a15-e3f7650480f0/evaluate-scopes/generate-example-access-token?userId=eedecfd8-dac7-42a9-863a-28c270b57918&scope=openid&audience=grafana_dlqlr63acgdf

2026-08-11 07:46:20,399 DEBUG [org.keycloak.services.resources.admin.ClientScopeEvaluateResource] (executor-thread-2) generateExampleAccessToken invoked. User: justin.lee, Scope param: openid, Target Audience: grafana_dlqlr63acgdf

2026-08-11 07:46:20,428 DEBUG [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-2) Error response Not Found: jakarta.ws.rs.NotFoundException: Requested audience not available: grafana_dlqlr63acgdf
        at org.keycloak.services.resources.admin.ClientScopeEvaluateResource.validateAudience(ClientScopeEvaluateResource.java:416)
        at org.keycloak.services.resources.admin.ClientScopeEvaluateResource.lambda$generateExampleAccessToken$5(ClientScopeEvaluateResource.java:286)
        at org.keycloak.services.resources.admin.ClientScopeEvaluateResource.sessionAware(ClientScopeEvaluateResource.java:384)
        at org.keycloak.services.resources.admin.ClientScopeEvaluateResource.generateExampleAccessToken(ClientScopeEvaluateResource.java:281)
        at org.keycloak.services.resources.admin.ClientScopeEvaluateResource$quarkusrestinvoker$generateExampleAccessToken_fc7d9244a5e406c75f367614e7dd45fe6668ca68.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at org.keycloak.quarkus.runtime.integration.resteasy.TransactionalSessionHandler.handle(TransactionalSessionHandler.java:85)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:190)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:677)
        at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2651)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2630)
        at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1622)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1589)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)

Grafana log 分析

  • Grafana 作為 client,登入時從 ID Token 與 Access Token 抽取 user info(log 中 source=id_token、source=access_token 各一次),但不校驗 access token 的 aud —— 該校驗是 resource server 的職責。因此 aud 不含 grafana_dlqlr63acgdf 不影響登入
logger=oauth.generic_oauth t=2026-08-11T06:04:18.953158393Z level=debug msg="Extracting user info from OAuth ID token"
logger=oauth.generic_oauth t=2026-08-11T06:04:18.95359253Z level=debug msg="Parsed user info from JSON" raw_json="{\"exp\":1786428558,\"iat\":1786428258,\"auth_time\":1786428255,\"jti\":\"11111111-1111-1111-1111-111111111111\",\"iss\":\"https://keycloak.example.com/realms/master\",\"aud\":\"grafana_dlqlr63acgdf\",\"sub\":\"22222222-2222-2222-2222-222222222222\",\"typ\":\"ID\",\"azp\":\"grafana_dlqlr63acgdf\",\"sid\":\"M8qwAEmOniUT8saescie1qTN\",\"at_hash\":\"xTisnruSVfR4wgTlmHyLMw\",\"acr\":\"1\",\"resource_access\":{\"grafana_dlqlr63acgdf\":{\"roles\":[\"grafana-admin\"]}},\"email_verified\":true,\"name\":\"lee justin\",\"preferred_username\":\"justin.lee\",\"given_name\":\"lee\",\"family_name\":\"justin\",\"email\":\"email@gmail.com\"}" data="Name: lee justin, Displayname: , Login: , Username: , Email: email@gmail.com, Upn: , Attributes: map[]" source=id_token

logger=oauth.generic_oauth t=2026-08-11T06:04:19.207993071Z level=debug msg="Extracting user info from OAuth access token"
logger=oauth.generic_oauth t=2026-08-11T06:04:19.208450198Z level=debug msg="Parsed user info from JSON" raw_json="{\"exp\":1786428558,\"iat\":1786428258,\"auth_time\":1786428255,\"jti\":\"ofrtac:35af2004-2afb-c99c-c5d7-8219a72b7be8\",\"iss\":\"https://keycloak.example.com/realms/master\",\"aud\":[\"account\"],\"sub\":\"22222222-2222-2222-2222-222222222222\",\"typ\":\"Bearer\",\"azp\":\"grafana_dlqlr63acgdf\",\"sid\":\"M8qwAEmOniUT8saescie1qTN\",\"acr\":\"1\",\"allowed-origins\":[\"https://grafana.example.com\"],\"realm_access\":{\"roles\":[\"default-roles-master\",\"offline_access\",\"uma_authorization\"]},\"resource_access\":{\"grafana_dlqlr63acgdf\":{\"roles\":[\"grafana-admin\"]},\"account\":{\"roles\":[\"manage-account\",\"manage-account-links\",\"view-profile\"]}},\"scope\":\"openid offline_access email profile\",\"email_verified\":true,\"name\":\"lee justin\",\"preferred_username\":\"justin.lee\",\"given_name\":\"lee\",\"family_name\":\"justin\",\"email\":\"email@gmail.com\"}" data="Name: lee justin, Displayname: , Login: , Username: , Email: email@gmail.com, Upn: , Attributes: map[]" source=access_token

結論

  • Evaluate 以自身 client id 作為 target audience 時,Keycloak 在 validateAudience 就中止並回 404,token 未被產生。錯誤描述的是「查無此 audience 設定」,不是「產生的 token 受眾不符」。
  • Grafana 是 client 而非 resource server,從未校驗 access token 的 aud。
  • 本環境目前沒有任何 resource server 在消費 access token,aud 的內容因此尚未產生實際效果。
  • 註:本文 log 已去識別化,jti、sub、sid、domain、email 為替換值;其餘未列出者均為原值。