Initial experimental support for Resource Indicators#46763
Initial experimental support for Resource Indicators#46763stianst merged 2 commits intokeycloak:mainfrom
Conversation
5a2655b to
8fc755b
Compare
|
|
||
| @Override | ||
| public AccessToken intercept(AccessToken accessToken, ClientSessionContext clientSessionCtx) { | ||
| if (!Profile.isFeatureEnabled(Profile.Feature.RESOURCE_INDICATORS)) { |
There was a problem hiding this comment.
This check seems a bit redundant? As ResourceIndicatorsInterceptorFactory is enabled just if the feature RESOURCE_INDICATORS feature is enabled, so it is already checked at the "factory" level.
There was a problem hiding this comment.
Good point, will remove from the updates
| try { | ||
| responseBuilder = tokenManager | ||
| .responseBuilder(realm, client, event, session, userSession, clientSessionCtx).generateAccessToken(); | ||
| } catch (TokenInterceptorException e) { |
There was a problem hiding this comment.
There are few more places from where tokenManager.responseBuilder(..).generateAccessToken() is invoked. I suppose you want to address that as a follow-up?
There was a problem hiding this comment.
This was mostly just temporary. I've moved it to be handled in TokenEndpoint#processGrantRequest so it doesn't have to be handled in each grant separately.
| for (String audience : accessToken.getAudience()) { | ||
| ClientModel client = session.clients().getClientByClientId(session.getContext().getRealm(), audience); | ||
| if (client != null) { | ||
| String clientResourceUrl = client.getAttribute("resource_url"); |
There was a problem hiding this comment.
Question: Do we want to support explicit client attribute like resource_url ? Or do we eventually want to support also something like "Base URL" of the client as fallback in case that resource_url attribute is not set?
I recall in the original proposal google docs, there was suggestion to use "Base URL" (or "Root URL") of a client as a fallback. But maybe this is not needed and for the sake of simplicity and safety, we can rather always require resource_url client attribute?
There was a problem hiding this comment.
For now I'm just using a custom attribute, but will add a follow-up to do something more proper.
| } | ||
| } else { | ||
| for (String audience : accessToken.getAudience()) { | ||
| ClientModel client = session.clients().getClientByClientId(session.getContext().getRealm(), audience); |
There was a problem hiding this comment.
I recall we discussed also the option when aud claim in the token would contain directly the URL itself (instead of clientId). So in that case, we will possibly need something like "lookup client by resource-url" in addition getClientByClientId . But maybe something as a follow-up?
There was a problem hiding this comment.
Yes, so it supports either urn:client:myclient or https://theclientendpoint now. This should be a bit more clear in the changes I'm about to push
| .collect(Collectors.toSet())); | ||
| } | ||
|
|
||
| session.getAllProviders(TokenInterceptorProvider.class).forEach(i -> i.intercept(accessToken, clientSessionCtx)); |
There was a problem hiding this comment.
The TokenInterceptorProvider currently has method signature lik:
AccessToken intercept(AccessToken token, ClientSessionContext clientSessionCtx);
So it is supposed to return the AccessToken . However it seems this invocation ignores the returned value and rather always assumes that original AccessToken instance was updated.
Would be better to either replace accessToken with returned accessToken or eventually change the signature of TokenInterceptorProvider to return void to make it more clear that interceptor can update existing accessToken instance.
There was a problem hiding this comment.
Changed this around quite a bit and it's now invoked in TokenManager#build
There was a problem hiding this comment.
Looking at the way that the TokenInterceptor is currently applied, it seems to function as some kind of token "post processing" where we can modify the current state of a token before it is encoded.
When I initially read the term TokenInterceptor I thought this would allow to pre/postprocess or replace a token.
Perhaps a name like TokenPostProcessor would be closer to the actual usage.
There was a problem hiding this comment.
Yeah, that is probably a better name. Will rename it.
While doing this I realised that it's a bit of a PITA to add this kinda stuff; and there's code all over the place for different OAuth capabilities. Might be good to at some point introduce some sort of OAuthGrantInterceptor that can expose params, handle params, update tokens, etc. for all different grants. Then in theory a lot of the stuff that is repeated all over the place could be centralized per capability/param.
5737b35 to
e87f184
Compare
Closes keycloak#47040 Signed-off-by: stianst <[email protected]> # Conflicts: # services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java # tests/utils-shared/src/main/java/org/keycloak/testsuite/util/oauth/LoginUrlBuilder.java
Signed-off-by: stianst <[email protected]>
a9b2ad0 to
4308fc7
Compare
rmartinc
left a comment
There was a problem hiding this comment.
Thanks @stianst! For me it's OK as starting point. But I would say that we need the following follow-ups:
- We need to check the resource parameter is OK. The must checks in the spec (absolute, no fragments,...). We can extend with client policies if needed for custom checks or the should ones.
- Extend for several resources instead of one.
- Changes in the admin UI to include the new attributes.
With this PR, we don't need any boolean in the requester client to say that the client wants to use resource indicators. It just uses resource syntax if the resource parameter is included in the request. If no resource param, it acts as before. For me it's OK but I understood that we didn't want this in the meetings.
| } | ||
|
|
||
| String requestedResource = clientSessionCtx.getAttribute(OAuth2Constants.RESOURCE, String.class); | ||
| if (requestedResource == null) { |
There was a problem hiding this comment.
Probably the config option to enable the resource at client level should be checked here in the first PR. Note that if enabled, all URLs should be returned for the client if the resource passed is null, or some default URLs at least, not null.
There was a problem hiding this comment.
|
|
||
| protected TokenManager.AccessTokenResponseBuilder createTokenResponseBuilder(UserModel user, UserSessionModel userSession, ClientSessionContext clientSessionCtx, String scopeParam, Function<TokenManager.AccessTokenResponseBuilder, ClientPolicyContext> clientPolicyContextGenerator) { | ||
| clientSessionCtx.setAttribute(Constants.GRANT_TYPE, context.getGrantType()); | ||
| clientSessionCtx.setAttribute(OAuth2Constants.RESOURCE, formParams.getFirst(OAuth2Constants.RESOURCE)); |
There was a problem hiding this comment.
Here we need to check the resource parameter following the spec.
There was a problem hiding this comment.
Need to find a way to verify the resource parameter properly, but it's not straightforward as there are multiple places that will set the resource parameter as well as I'd like to extract as much as possible into a separate provider. I'd like to handle this as a follow-up in #47116
|
|
||
| private String findAudienceByClientUrn(String resource, String[] audience) { | ||
| String requestedClientId = resource.substring(URN_CLIENT_PREFIX.length()); | ||
| return find(requestedClientId, audience); |
There was a problem hiding this comment.
@stianst This means when resource urn:client:xxxx the audience is just set to xxxx, right? yes, I see the same in the tests. For me it's OK although it surprised me initially.
There was a problem hiding this comment.
Yes, so the thinking was that urn:client was a special case to support filtering audiences as is. We pretty much know that no clients will be expecting the special urn:client: in the audience, but at the same time it needs to be a URI in the resource parameter. Will make sure to document this as I agree it may be a bit unexpected. Added a note to #47127 to make sure it's covered in the docs.
I think we want to be explicit about enabling resource indicators for RPs, but I've added this as a follow-up to #47121 - it may also not be needed in the end as I'm not sure there is any harm in enabling resource params always, but that may depend a bit on what we do in #47126 |
mposolda
left a comment
There was a problem hiding this comment.
Thanks for the updates. Figured just one additional issue (Up to you if you prefer to add this as a follow-up or directly in this PR).
|
|
||
| String grantType = context.clientSessionCtx().getAttribute(Constants.GRANT_TYPE, String.class); | ||
|
|
||
| if (grantType.equals(OAuth2Constants.REFRESH_TOKEN)) { |
There was a problem hiding this comment.
Regarding the refreshToken request, there is the possible issue that originalAudience from refresh token is not propagated back to the newly issued access-token in this PR.
As for example when accessToken will have multiple audiences after processing of protocol mappers (EG. theservice and theservice2), then the refreshed access token will still contain both of these audiences instead of the "original audience" from refresh-token. This is not the desired behaviour IMO?
Just a note that this issue is not visible in your automated tests as they use single audience theservice to be in the access-token after the protocol mappers processing, so it seems it is not tested that some undesired audiences should be filtered from the access token.
There was a problem hiding this comment.
I wasn't initially planning on including refresh token request in this PR at all, but ended up adding it, but it's not completed. I forgot to add a follow-up task around this and added #47180 now to capture this case.
Would it be okay to handle it as a follow-up?
mposolda
left a comment
There was a problem hiding this comment.
Approving the PR, considering that we have follow-up issues to handle remaining stuff
Initial support for resource indicators by filtering token audience as proposed in:
#35743 (comment)
This is just the start and I've created a list of follow-ups in #47040 (comment) will convert these to sub-tasks of #14355 once this PR is merged. For those reviewing it would be good to capture anything else that is missing, a comment in this PR would be fine (I'll create tasks afterwards).
Signed-off-by: stianst [email protected]