Enhancing Light Weight Token#22148
Conversation
|
The discussion can be found in #9713 . |
5b813f9 to
fec60ab
Compare
|
We believe that token introspection should return all access token information enhanced by mappers with token introspection equal to true.
|
|
I propose to add tests with most protocol mappers with Add to access token false and Add to token introspection false and true. Claims must be showed in token introspection only if Add to token introspection is true. Moreover, add special case with Add to access token false and Add to token introspection true for Attribute Mapper for special access token cases ( offline access token, token from client credentials , token form OAuth token exchange). |
There was a problem hiding this comment.
What is the scope of this mapper? Does it needed?
I believe that if implemented, logic to get access token from singleUseStore must be implemented to all OAuth -OIDC flows that access token is a query parameter. Fe UserInfo, refresh flow.
What happen in singleUseStore during Keycloak restart? This is important for offline access tokens.
Although we want support for lightway access token and enhance token introspection, we believe that there is no need to extra space for saving access token. Access token could have only needed information for Userinfo - token introspection, scope and other required claims based on OAuth specification.
This could support with appropritate protocol mappers ( Add to access token false and Add to token introspection true).
There was a problem hiding this comment.
+1
I am also not very convinced if LightWeightAccessTokenMapper is needed.
Access token could have only needed information for Userinfo - token introspection, scope and other required claims based on OAuth specification.
+1 to that, however access token does not even need to have all required claims based on OAuth specification as OAuth/OIDC does not prescribe that access token must be JWT. Access token can be opaque token, not even JWT. So IMO it can be possible to even omit some JWT-required claims from access token, but it should be carefully considered which ones to support all the endpoints as you mentioned.
There was a problem hiding this comment.
I agree.
I am reffering to JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens documentation. However, this discussion is out of scope of this PR.
There was a problem hiding this comment.
@cgeorgilakis @mposolda I think that the intention of introducing LightWeightAccessTokenMapper is to hide such claims from an access token that a protocol mapper cannot control.
Also it seems that you are worry about putting a lot of information onto SingleUseObjectProvider.
IMO, the following claims are only available when Keycloak receives a token request from a client and generate an access token:
- scope, nonce, jti, iat, exp, client_id, azp, session_state, iss(realm name), cnf (OAuth MTLS, DPoP)
Therefore, if we want not to put such claims onto an access token but want to put them onto a token introspection response, we need to keep them by some mechanism like SingleUseObjectProvider.
One option of a lightweight access token is that we must put all such claims to an access token. Another option is that we only put some of such claims to an access token and others are kept by using SingleUseObjectProvider by this LightWeightAccessTokenMapper . WDYT?
There was a problem hiding this comment.
I see the idea behind the mapper but I don't think we need it at all. It makes more sense to do the opposite: provide specific mappers for those claims and then configure them accordingly.
For me, most of the claims being removed by this method are a key to accessing protected resources on a resource server. We can't be opinionated on which claims should go into the access token as it is use-case-specific. For some, you might need the subject in a lightweight token when accessing protected resources, or even the client to which the token was issued.
We should keep in mind that a lightweight token is just about making sure you have minimal claims to avoid introspecting tokens. The more claims you remove, the more your access token becomes a reference token instead, for which you are forced to introspect the token in order to resolve additional information at the resource server when processing bearer tokens.
There was a problem hiding this comment.
I am reffering to JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens documentation.
You have a good point here and we should probably choose the basic claims a lightweight token should have based on this spec. It already provides the minimal set (similar to ID Token) of claims and that spec was based on feedback from different vendors and use cases.
There was a problem hiding this comment.
This code only needed if lightWeightAccessTokenMapper is enabled. Please move it in line 121 ( if (tokenData != null) { ).
In common cases lightway access token would be enabled using Add to token introspection field.
This information is containing to access token. So this code is dublicated.
There was a problem hiding this comment.
One question is, if we need the LightweightAccessTokenMapper at all or if we can remove it from this PR as mentioned in the other threads? I am not 100% sure about it... If not needed, then we can remove this code altogether from this PR.
There was a problem hiding this comment.
We do not want this mapper. @mposolda you should decide about having it.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Good catch.
It is possible it may be needed to extract some methods from UserInfoEndpoint (or other classes) to deal with transient user sessions to some generic util class?
There was a problem hiding this comment.
I agree. We can make UserInfoEndpoint.findValidSession public or move it to util class
There was a problem hiding this comment.
UserInfoEndpoint.findValidSession has been Utilized (UserSessionUtil) so that it can also be referenced by the AccessTokenIntrospectionProvider.
There was a problem hiding this comment.
UserInfoEndpoint.findValidSession has been Utilized (UserSessionUtil) so that it can also be referenced by the AccessTokenIntrospectionProvider.
Now it is correct. I only believe that we should catch method errors and together with user being null return null in this method.
So token introspection will return false - not exception.
mposolda
left a comment
There was a problem hiding this comment.
I've added few comments inline. Some more comments here:
-
I am not 100% sure if we need
LightweightAccessTokenMapperclass? I guess that using this functionality and storing tokens in the "singleUseStore" has additional price-to-pay and also additional places where it would need to be handled as mentioned by @cgeorgilakis. So far, I vote for removing it from this PR and also removing related code fromAccessTokenIntrospectionProvider. But I am curious for more opinions here... -
We need to handle backwards compatibility. In previous version, all claims from access token were added to introspection response. So:
- It will be good if
Include in Introspection endpointswitch is ON by default for all protocol mappers whereInclude in access tokenis ON by default. - Will be good to handle protocol mappers in all built-in client scopes (added by OIDC protocol factory) and pre-configured scopes
- I guess that method
public static ProtocolMapperModel createClaimMapper(String name, String userAttribute, String tokenClaimName, String claimType, boolean accessToken, boolean idToken, boolean userinfo, String mapperId)
can be good to update, so it has also boolean parameterboolean introspectionEndpointas parameter
and the other methods can call this with same value like they use foraccessToken? - Also I guess that method
OIDCAttributeMapperHelper.includeInIntrospectioncan be updated to handle backwards compatibility in similar way like we use inOIDCAttributeMapperHelper.includeInUserInfo()? I guess something like:
- It will be good if
public static boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null && includeInAccessToken(mappingModel)) {
return true;
}
return "true".equals(includeInIntrospection);
}
- It can be very nice if admin console shows this switch ON by default in case that value is not present
There was a problem hiding this comment.
+1
I am also not very convinced if LightWeightAccessTokenMapper is needed.
Access token could have only needed information for Userinfo - token introspection, scope and other required claims based on OAuth specification.
+1 to that, however access token does not even need to have all required claims based on OAuth specification as OAuth/OIDC does not prescribe that access token must be JWT. Access token can be opaque token, not even JWT. So IMO it can be possible to even omit some JWT-required claims from access token, but it should be carefully considered which ones to support all the endpoints as you mentioned.
There was a problem hiding this comment.
Good catch.
It is possible it may be needed to extract some methods from UserInfoEndpoint (or other classes) to deal with transient user sessions to some generic util class?
There was a problem hiding this comment.
One question is, if we need the LightweightAccessTokenMapper at all or if we can remove it from this PR as mentioned in the other threads? I am not 100% sure about it... If not needed, then we can remove this code altogether from this PR.
+1 |
@cgeorgilakis I think that switch One additional point: I guess that some clients in the realm may want lightweight access tokens, when some other clients may not. But due the fact that But anyway, I vote for |
|
@tnorimat Could you please review as well, unless you did already? |
|
@mposolda Yes, I will review the PR. |
I prefer to use different custom scopes for the cases described. |
Added a test case for the above viewpoint to LightWeightAccessTokenMapperTest. |
A boolean introspection was added to the createClaimMapper and create methods of the protocol mapper, and the introspection setting value was set to be the same as the accessToken. In order to maintain OIDCAttributeMapperHelper's includeIntrospection backwards compatibility, if nothing is set, the value of includeInAccessToken is returned. |
There was a problem hiding this comment.
In the case of client credential flow, the session becomes TRANSIENT and the sid is set to null in the processing of the tokenEndpoint after the protocol mapper, so I tried not to set it to SingleUseStore here. Also, since sub is required to get a TRANSIENT session with findValidSession of the introspection endpoint, it is now set to SingleUseStore.
|
@cgeorgilakis @mposolda The points you've mentioned are treated. Could you check the revised PR? |
There was a problem hiding this comment.
I believe that it is good not showing such changes in PR
There was a problem hiding this comment.
This is the verifyAccessToken method except the return statement.
I propose create a new method used here and in verifyAccessToken method.
If verification failed now Keycloak return false. With your code an exception will be thrown.
I believe that we should keep same behaviour and return here null.
I believe that PR has been improved a lot. Thanks @skabano for the tests.
See my comments for details. |
c106ffb to
ea9fd82
Compare
I've fixed the above point. |
Thanks.
|
ea9fd82 to
8fc7754
Compare
|
@cgeorgilakis
The findValidSession method throws the error if an error occurs. So, create a method (verifyAccessToken) to check in advance before calling findValidSession method. If token is not valid, transformAccessToken returns null. Is this correct?
Are you saying that the User endpoint should make similar changes to TokenIntrospection? |
I am reffering to line 235 of UserInfoEndpoint class. I wonder if we need also this check in token introspection. @mposolda what do you believe for this? |
8fc7754 to
eec911a
Compare
Thank you. My understanding was wrong. |
|
So, the PR is ok for me. |
ghost
left a comment
There was a problem hiding this comment.
Unreported flaky test detected, please review
Unreported flaky test detectedIf the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.ui.account2.SessionTest#welcomeScreenSsoTimeoutTestKeycloak CI - Account Console IT (chrome) |
6b0761d to
89ca1e9
Compare
89ca1e9 to
d139f2d
Compare
d139f2d to
106bee9
Compare
ghost
left a comment
There was a problem hiding this comment.
Unreported flaky test detected, please review
Unreported flaky test detectedIf the below flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.x509.X509BrowserCRLTest#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststoreKeycloak CI - FIPS IT (non-strict) |
| @@ -0,0 +1,691 @@ | |||
| { | |||
There was a problem hiding this comment.
Is it possible to remove this file from your PR and make sure that the Lightweight token test is based on the default file from testsuite/integration-arquillian/tests/base/src/test/resources/testrealm.json ? It looks that there are almost no changes between your file and the basic testsuite file. If any modifications are needed in the test, it is possible to either add them to the LightWeightAccessTokenTest.addTestRealms method (you already did some modifications here) or use admin client and update before/after particular test methods.
Sorry I missed this in previous reviews (if it was present). Having many various testrealm-*.json files means bigger work in the future when some testsuite maintenance is needed, so it is good to avoid adding more such files unless really needed.
There was a problem hiding this comment.
I removed mapper-test/testrealm.json and added it to addTestRealms.
There was a problem hiding this comment.
+1 to your proposal and to the PR https://github.com/Hitachi/keycloak/pull/431/files .
One question: Did you manually check if migration work as expected? It can be good to verify. The steps are:
- Start some older Keycloak (maybe 22.0.4) configured against some DB (for instance MySQL)
- Stop the Keycloak 22.0.4 server
- Start the Keycloak server built with your PR configured against same DB as was used in step 1
- Make sure that access token obtained contains "web origins" and "audience" as expected. It can be verified for instance in the admin console by showing any random client (EG.
account-console) and then going to tabClient Scopesand subtabevaluateof that client.
c602789 to
d30ec82
Compare
|
@mposolda |
|
@skabano Thanks a lot and congratulations to your 1st great contribution! It was quite a long journey and quite a few challenges, but appreciate your patience. @cgeorgilakis @tnorimat @pedroigor Thanks everyone for participate in the discussion on this PR |
Light Weight Token has been implemented in the following specifications.
About the value set by the TokenManager initToken in the access token
Create a new LightWeightAccessTokenMapper protocol mapper and removed the value.
The deleted value is set in AccessTokenIntrospectionProvider so that it can be retrieved by Introspection endpoint.
About the value set by the Protocol mapper to the access token
Removed the value from the access token by turning OFF the Add to access token for each protocol mapper.
Furthermore, by implementing the following more flexibility for Introspection endpoint, by turning ON the Add to token Introspection,
it is possible to obtain the value in Introspection endpoint.
More flexibility for Introspection endpoint
#21183