You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I would like to discuss a behavior that seems inconsistent to me after the audience validation change introduced in Keycloak 26.6.2 for the OAuth2 token introspection endpoint.
TL;DR
Since Keycloak 26.6.2, the introspection endpoint requires the introspecting client to be present in the token's aud claim.
However, Audience Resolve automatically adds other clients to aud when their roles are present in the access token, but it does not do the same for the issuing client itself, even if the token contains roles for that client.
As a result, a client can receive an access token that contains its own client roles, but cannot introspect that token by default because it is only represented as azp, not as aud.
My preferred direction would be to make access token content explicitly resource server scoped and align aud with the clients whose roles are actually included in the token. I would like to understand whether there are security, compatibility, or specification reasons against such a change that I am missing.
Context
As described in the migration guide, the introspection endpoint now validates that the authenticated client is present in the token's aud claim. If the authenticated client is not contained in the audience, introspection returns {"active": false}.
I understand and agree with the security motivation behind this change. A client should not be able to introspect arbitrary tokens that were not intended for it. This makes sense, especially for lightweight access tokens and for preventing claim leakage across clients.
However, this change seems to expose an existing inconsistency in the default token model.
Keycloak currently has the Audience Resolve behavior where audiences of clients are automatically added to the aud claim when the authenticated user has at least one client role for those clients in the token. In other words, client roles in the access token are used as an indicator that the corresponding client is a relevant audience.
At the same time, the client for which the token was issued is treated differently. Even if the access token contains roles for that very client, the client itself is not automatically added to the aud claim. It is only represented as azp.
Before 26.6.2, this distinction was mostly a semantic detail. Since 26.6.2, it has practical consequences:
Client my-client obtains an access token.
The token contains roles for my-client in resource_access.
The token does not contain my-client in aud.
my-client calls the introspection endpoint to validate its own access token.
Introspection returns {"active": false} because my-client is not in the audience.
This feels surprising because the token contains authorization information for my-client, but my-client is not allowed to introspect it unless an additional audience mapper is configured.
Two possible consistent models
From my point of view, there are two internally consistent models.
Alternative 1: Treat access token content as explicitly resource server scoped
In this model, Keycloak would not assume that roles for the issuing client are always relevant to the access token.
Client roles would only appear in the access token if they are explicitly included through the effective client scopes. If roles for a client are included in the access token, this would mean that the token contains authorization information relevant to that client as a resource server.
Then Audience Resolve could consistently apply the same rule to all clients, including the issuing client:
If client roles for a client are present in the access token, the corresponding client ID is added to aud.
This would make the model easier to reason about:
Roles for a client in the access token mean that the token is relevant for that client.
A client for which the token is relevant is included in aud.
A client included in aud can introspect the token.
No special case is needed for the issuing client.
I personally prefer this model because it keeps the access token resource server oriented. It also avoids treating azp as an implicit audience. The token content and the audience claim would be aligned.
Alternative 2: Treat the issuing client as implicitly relevant if its own roles are included
The other consistent model would be to keep the current behavior where roles for the issuing client can appear in the access token by default.
In that case, I think the issuing client should also be treated as a valid audience when its own roles appear in the token.
This could be implemented either by automatically adding the issuing client to aud when its own client roles are present, or by allowing self introspection when the authenticated introspecting client matches azp.
This model would preserve the existing legacy behavior more closely. It would also match the expectation that if an access token contains roles for my-client, then my-client should be able to validate that token through introspection.
However, I like this model less than Alternative 1 because it makes the issuing client special and weakens the clean separation between azp and aud.
Why the current behavior feels inconsistent
The current behavior seems to combine parts of both models:
Client roles in the token imply audience relevance for other clients.
Client roles in the token do not imply audience relevance for the issuing client.
The issuing client is represented as azp, but azp is not sufficient for introspection.
As a result, a client may receive an access token containing its own roles but still cannot introspect that token by default.
I understand that this can be fixed by adding an explicit audience mapper. However, the question is whether this should be necessary in the specific case where the token already contains roles for the introspecting client.
Question
Is there a security or specification reason why the issuing client must be excluded from Audience Resolve even when the access token contains roles for that client?
Would it be reasonable to consider changing the default behavior toward Alternative 1, where client roles are only included when explicitly brought into the token through client scopes, and Audience Resolve consistently adds every client whose roles are present in the access token, including the issuing client?
Or is there a design reason I am missing that makes the current distinction between the issuing client and all other clients necessary?
I am not suggesting that introspection should become less strict again. The audience check introduced in 26.6.2 makes sense to me. My question is whether the token generation side should be made more consistent with that stricter validation model.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Hi Keycloak team,
I would like to discuss a behavior that seems inconsistent to me after the audience validation change introduced in Keycloak 26.6.2 for the OAuth2 token introspection endpoint.
TL;DR
Since Keycloak 26.6.2, the introspection endpoint requires the introspecting client to be present in the token's
audclaim.However, Audience Resolve automatically adds other clients to
audwhen their roles are present in the access token, but it does not do the same for the issuing client itself, even if the token contains roles for that client.As a result, a client can receive an access token that contains its own client roles, but cannot introspect that token by default because it is only represented as
azp, not asaud.My preferred direction would be to make access token content explicitly resource server scoped and align
audwith the clients whose roles are actually included in the token. I would like to understand whether there are security, compatibility, or specification reasons against such a change that I am missing.Context
As described in the migration guide, the introspection endpoint now validates that the authenticated client is present in the token's
audclaim. If the authenticated client is not contained in the audience, introspection returns{"active": false}.I understand and agree with the security motivation behind this change. A client should not be able to introspect arbitrary tokens that were not intended for it. This makes sense, especially for lightweight access tokens and for preventing claim leakage across clients.
However, this change seems to expose an existing inconsistency in the default token model.
Keycloak currently has the Audience Resolve behavior where audiences of clients are automatically added to the
audclaim when the authenticated user has at least one client role for those clients in the token. In other words, client roles in the access token are used as an indicator that the corresponding client is a relevant audience.At the same time, the client for which the token was issued is treated differently. Even if the access token contains roles for that very client, the client itself is not automatically added to the
audclaim. It is only represented asazp.Before 26.6.2, this distinction was mostly a semantic detail. Since 26.6.2, it has practical consequences:
my-clientobtains an access token.my-clientinresource_access.my-clientinaud.my-clientcalls the introspection endpoint to validate its own access token.{"active": false}becausemy-clientis not in the audience.This feels surprising because the token contains authorization information for
my-client, butmy-clientis not allowed to introspect it unless an additional audience mapper is configured.Two possible consistent models
From my point of view, there are two internally consistent models.
Alternative 1: Treat access token content as explicitly resource server scoped
In this model, Keycloak would not assume that roles for the issuing client are always relevant to the access token.
Client roles would only appear in the access token if they are explicitly included through the effective client scopes. If roles for a client are included in the access token, this would mean that the token contains authorization information relevant to that client as a resource server.
Then Audience Resolve could consistently apply the same rule to all clients, including the issuing client:
If client roles for a client are present in the access token, the corresponding client ID is added to
aud.This would make the model easier to reason about:
aud.audcan introspect the token.I personally prefer this model because it keeps the access token resource server oriented. It also avoids treating
azpas an implicit audience. The token content and the audience claim would be aligned.Alternative 2: Treat the issuing client as implicitly relevant if its own roles are included
The other consistent model would be to keep the current behavior where roles for the issuing client can appear in the access token by default.
In that case, I think the issuing client should also be treated as a valid audience when its own roles appear in the token.
This could be implemented either by automatically adding the issuing client to
audwhen its own client roles are present, or by allowing self introspection when the authenticated introspecting client matchesazp.This model would preserve the existing legacy behavior more closely. It would also match the expectation that if an access token contains roles for
my-client, thenmy-clientshould be able to validate that token through introspection.However, I like this model less than Alternative 1 because it makes the issuing client special and weakens the clean separation between
azpandaud.Why the current behavior feels inconsistent
The current behavior seems to combine parts of both models:
azp, butazpis not sufficient for introspection.I understand that this can be fixed by adding an explicit audience mapper. However, the question is whether this should be necessary in the specific case where the token already contains roles for the introspecting client.
Question
Is there a security or specification reason why the issuing client must be excluded from Audience Resolve even when the access token contains roles for that client?
Would it be reasonable to consider changing the default behavior toward Alternative 1, where client roles are only included when explicitly brought into the token through client scopes, and Audience Resolve consistently adds every client whose roles are present in the access token, including the issuing client?
Or is there a design reason I am missing that makes the current distinction between the issuing client and all other clients necessary?
I am not suggesting that introspection should become less strict again. The audience check introduced in 26.6.2 makes sense to me. My question is whether the token generation side should be made more consistent with that stricter validation model.
Thanks for considering this.
All reactions