Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@danielFesenmeyer
Copy link
Contributor

This is an implementation DRAFT based on discussion #8599. I would like to get feedback from the community, whether the implemented approach makes sense, how it can be improved, or if someone suggests a better alternative.

Current Implementation

This DRAFT PR implements a mapper named AccessTokenShortenerMapper, which can be configured with a list of "Introspectable claims", which will be excluded from the resulting access token - the default value for those claims is "aud,realm_access.roles,resource_access". Instead of those claims, the token contains a special claim "isc" (short for introspectable claims), which contains the claims which have been excluded.
The "Introspectable claims" are configurable, in order to make it possible to also exclude groups or custom claims, which may become large.

The token introspection endpoint has been extended to evaluate the "isc" claim and extending the existing token with the missing claims. The missing claims are created based on a newly created access token. This access token is created based on user info, client info and scope from the existing token, skipping AccessTokenShortenerMapper.

The KeycloakIdentity class, which is used in the Admin API and several other places, has been adjusted to support reloading the missing claims (with "internal token introspection"), when an access token contains the "isc" claim.
This way, the Admin API can be used with any full-scope client with configured AccessTokenShortenerMapper - even when the full token is quite large - for example, when authorizing as an admin of many realms.

Implications of the current implementation

The current implementation of the token introspection (both the "actual" and the "internal" token introspection) extends the token with the missing claims based on the current state, not the state when the token was created. This should be fine, because the current state should be the relevant one for most use cases.
On the other hand, for performance reasons, it COULD make sense to cache a full token - then the token introspection would not necessarily reflect the current state.

Considered alternatives

Special admin-client flag on the client

This should be quite easy to implement. When this flag would be set, the Admin API would just load the roles from the persistence (see UserModelIdentity), as it is already done for the special clients admin-cli and security-admin-console.

But this approach has the drawback that it is only usable when accessing Keycloak internal API. It does not help to integrate external clients which require a large amount of authorization data.

Add a flag to mappers

Add a flag to the mappers, which defines whether this mapper's result should be excluded from a access token and only be available via token introspection. For users, it would be more intuitive to have this configuration directly at the affected mapper, but it also would be more implementation effort - not only once, but also for new mappers.
Furthermore, the client configuration would be much more complicated, because the aud, realm_access and resource_access claims are created by mappers configured for the "roles" client scope, which is a default client scope. In order to have different mappers configured for the admin clients, another "introspectable-roles" scope would have to be created and added as default scope to the client, instead of the "roles" scope. Additionally, the three mappers would have to be added to the client with appropriate configuration. And users might be confused that there are now two OIDC scopes "roles" and "introspectable-roles", for essentially the same data.

Open issues

Denylist for certain claims

It should be made sure that certain claims such as alg, typ, iss cannot be excluded from the token, in a similar way as it is already done in OIDCAttributeMapperHelper.

Support "Internal token introspection" for complete Keycloak API

For a final implementation, it has to be considered that there are several other places using bearer authentication. They probably should also be extended to support the "internal token introspection" functionality:

  • IdentityBrokerService: uses AccessToken directly
  • AccountLoader: instantiates an Auth class based on the AccessToken
  • ClientRegistrationAuth: checks the roles in a similar way as done in KeycloakIdentity, but with completely different code

Maybe there are even more places?

Migration of existing clients admin-cli and security-admin-console?

Currently, in case of the special admin clients, all (role-based) authorization is done based on the roles currently assigned to the user, regardless of whether they are contained in the token or even in the scope of the client (see UserModelIdentity).
To get rid of the UserModelIdentity and similar classes, the clients admin-cli and security-admin-console would need to be re-configured in the following way:

  • Change their scope to "Full Scope Allowed" (For admin clients - especially those for managing multiple realms - it is impractical to explicitly manage client roles)
  • Add the AccessTokenShortenerMapper to the clients, with the default configuration of "Introspectable claims": "aud,realm_access.roles,resource_access".

… the access token, which can be retrieved by token introspection
@thomasmicro
Copy link

Hi!
I just wonder about the difference between your extended introspection endpoint and the userinfo endpoint. Keycloak (as well as any other OAuth2 compliant IdP) supports fetching userdetails via this endpoint. Which details can be fetched can be specified in the Mappers or in the Client Scopes.

There is also some discussion about allowing to get access_token with additional Client Scopes applied using the /token-endpoint and refresh_token.

@danielFesenmeyer
Copy link
Contributor Author

Hi @thomasmicro, as I understand, the purpose of the userinfo endpoint is to provide profile information on the current user such as email, birthday etc. In Keycloak, per default, it does not contain role information. It's used in OIDC to retrieve more information about the user.
In contrast, the token introspection endpoint is used to introspect authorization information provided by (possibly opaque) tokens.
So I think the token introspection endpoint is the right place to get more information about an access token.

I found this anwer on Stackoverflow quite helpful: https://stackoverflow.com/a/59015391/1078445

@stianst stianst self-requested a review December 1, 2021 17:56
@stianst
Copy link
Contributor

stianst commented Dec 2, 2021

I'm not sure I quite follow this proposal. Is the AccessTokenShortenerMapper a special mapper that is used instead of other mappers, or in combination with other mappers?

Another option could be to add more options to mappers on what they are added to. Currently, there's support for "Add to ID token" and "Add to userinfo". What about if we also add "Add to Access Token" and "Add to Introspection"? That should solve the admin client use-case as the roles mapper can just be set to only "Add to Introspection", which is then also useful to other roles, as well as any other claims. This also would have the benefit of providing support for small tokens, which can remove the need to add support for things like reference tokens.

@stianst stianst requested a review from mposolda December 2, 2021 08:09
@danielFesenmeyer
Copy link
Contributor Author

I'm not sure I quite follow this proposal. Is the AccessTokenShortenerMapper a special mapper that is used instead of other mappers, or in combination with other mappers?

It is a special mapper which is used in combination with the other mappers - it's applied after all the other mappers. It removes configurable claims from the token which has been build by the other mappers.

Another option could be to add more options to mappers on what they are added to. Currently, there's support for "Add to ID token" and "Add to userinfo". What about if we also add "Add to Access Token" and "Add to Introspection"? That should solve the admin client use-case as the roles mapper can just be set to only "Add to Introspection", which is then also useful to other roles, as well as any other claims. This also would have the benefit of providing support for small tokens, which can remove the need to add support for things like reference tokens.

There is already an option "Add to Access Token" for some mappers (e.g. "realm roles"). But not for all mappers - some mappers just add the corresponding claim to the access token, without configuration option (e.g. "audience resolve"). The "Add to Introspection" option would have to be added and supported by all access-token related mappers.
I suppose it's not that easy to adjust that in a backwards-compatible way, but it should definitely be possible.
But the main drawback of this approach is that the existing "roles" scope could not be reused in the introspection scenario (e.g. admin clients), because different mapper configurations would be needed than in the "roles" scope (with "Add to Access Token" set to false instead of true).
I tried to explain that in the description, section Add a flag to mappers.

@jbman
Copy link
Contributor

jbman commented Dec 6, 2021

I also like the idea of an Add to Introspection flag. I try to line out which configuration would be delivered with Keycloak with this approach.

  • With an additional Add to Introspection flag, existing role mapper implementations would allow to select writing to token or introspection as configured. Existing default client roles and realm roles mapper would get a configuration which reproduces current behavior and writes to access token and introspection endpoint. The client scope roles would still use this mappers.
  • New mappers client roles introspection and realm roles introspection would be introduced together with a new client scope roles introspection. This scope is then used by admin clients. Overall this increases number of default mappers in Keycloak
  • Non-role-related mapper implementations could have the option to not support writing to access token or to introspection. A mapper like "audience resolve" would just declare mapping to introspection as not supported. Maybe a better version is contributed later if flexible configuration to token or introspection has any value

Could this be a valid approach @stianst?

@stianst
Copy link
Contributor

stianst commented Jan 27, 2022

We're trying to wrap this up as "lightweight access tokens", see #9713 for more details.

@MikaelElkiaer
Copy link

Some thoughts on this.

I am more familiar with the implementation of Reference Tokens in IdentityServer4.
This is an opaque token given to the user, configured on client level, while the server stores the underlying JWT.
The power of this is that you do not necessarily have to set a (short) expiration time, as the reference token can be manually revoked - as with a refresh_token. The caveat is that you rely fully on introspection in the resource server, effectively defining lifespan and change propagation based on caching of introspect calls. However, the reference token becomes a hybrid between an access token and a refresh token, removing the need of both frequent refresh requests as well as introspection requests. There might also be an additional caveat in propagating the stored JWTs in a distributed set-up - but I imagine it is the same if Keycloak stores users and/or access info.

Correct me if I am wrong, but with a lightweight token approach, the resource server would need to do both frequent refresh and introspect requests, where only the latter can be cached, right?

Also, it does not seem that this implementation utilizes token_type_hint for optimizing introspection, which could be worth considering. It is useful for revocation as well, in case Keycloak stores anything that needs to be cleared.

@MikaelElkiaer
Copy link

Digging deeper into what Keycloak can do in regards to tokens and claims, I also discovered the concept of Requesting Party Tokens. It seems that this actually solves some of my issues that could not be resolved with an ordinary access token.

@stianst
Copy link
Contributor

stianst commented Jul 17, 2023

I'm going to close this one for now as I don't like the custom/bespoke nature of it for admin endpoints. I'd say we should first prioritise getting lightweight access tokens included, then add support for that to the admin endpoints.

@stianst stianst closed this Jul 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants