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

Skip to content

Conversation

@rmartinc
Copy link
Contributor

Closes #43642

The PR creates a new intermediate class AbstractJWTValidator with part of the methods that previously were in AbstractJWTClientValidator. Class JWTAuthorizationGrantValidationContext in server-spi-private is now an interface (very limited now, but we can add mode methods if needed) and the real code has been moved to JWTAuthorizationJWTValidator in services which extends the AbstractJWTValidator to inherit the old methods. This way the code is shared. The signature validation is performed in the IdP side, and we can move more parts to the IdP if we want. So the current code is something in the middle. The token is validated using the client validator, but the signature is checked by the IdP. Draft for the moment. Questions:

  • Do you see this OK? Or do you prefer move signature to the client validator part? Or vice-versa, move token validations to the IdP side? It's a bit repeated in both sides.
  • The nbf claim is not checked now, I have just detected it. The client validator was not doing it before, so I will add later.
  • I have added the options we have currently in the IdP side (clockSkew and algorithm), but there are other options that we can add: token lifespan (default is 5m=300s) and reusePermitted (default to false). Do we want them or is it OK using fixed values for now?

I need to polish this a bit but the I want to hear your comments about the three questions before. 😄

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link

Unreported flaky test detected

If the 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.federation.ldap.LDAPProvidersIntegrationTest#updateLDAPUsernameTest

Keycloak CI - Base IT (5)

org.keycloak.testsuite.runonserver.RunOnServerException: java.lang.NullPointerException
	at org.keycloak.testsuite.client.KeycloakTestingClient$Server.fetchString(KeycloakTestingClient.java:185)
	at org.keycloak.testsuite.federation.ldap.LDAPProvidersIntegrationTest.updateLDAPUsernameTest(LDAPProvidersIntegrationTest.java:1656)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
...

Report flaky test

@rmartinc
Copy link
Contributor Author

I have changed some class names to be more appropriate. And the nbf is already managed in the isActive of the JsonWebToken. So point 2 is already done, just the first and the third one remain.

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link

Unreported flaky test detected

If the 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.federation.ldap.LDAPProvidersIntegrationTest#updateLDAPUsernameTest

Keycloak CI - Base IT (5)

org.keycloak.testsuite.runonserver.RunOnServerException: java.lang.NullPointerException
	at org.keycloak.testsuite.client.KeycloakTestingClient$Server.fetchString(KeycloakTestingClient.java:185)
	at org.keycloak.testsuite.federation.ldap.LDAPProvidersIntegrationTest.updateLDAPUsernameTest(LDAPProvidersIntegrationTest.java:1656)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
...

Report flaky test

@graziang
Copy link
Contributor

graziang commented Oct 27, 2025

@rmartinc Thanks! That’s pretty much how I imagined it, and I’m ok with having the signature validation on the IdP side.
I was wondering if there’s a way to avoid using ClientAssertionState in JWTAuthorizationGrantValidator, since ClientAssertionState was introduced to store a state to be propagated between authenticators and is not needed for the authorization grant. Maybe it could be moved to AbstractJWTClientValidator instead of AbstractBaseJWTValidator, or handled in some other way? Not sure if that’s possible.

@rmartinc
Copy link
Contributor Author

I think that the ClientAssertionState does not make any harm. It just contains the client requesting the assertion and the assertion info (jws, token, assertion and assertionType). So all the properties make sense for the JWT Authorization Grant. It's just another client autentication using an assertion. If we remove it, we need similar methods in the base class (getJWS, getToken,...). So I almost prefer to keep the state but no strong opinion here. @mposolda WDYT?

@mposolda mposolda requested a review from stianst October 29, 2025 08:27
@mposolda mposolda self-assigned this Oct 29, 2025
@rmartinc
Copy link
Contributor Author

Rebased with the last changes added by @graziang. 😄

Copy link
Contributor

@mposolda mposolda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmartinc Cool, Thanks!

I have few inline comments (Some of them are more a questions from discussion. Feedback welcome from you and @graziang as well).

Some answers to your questions:

Do you see this OK? Or do you prefer move signature to the client validator part? Or vice-versa, move token validations to the IdP side? It's a bit repeated in both sides.

I see this OK as it is now in your PR.

The nbf claim is not checked now, I have just detected it. The client validator was not doing it before, so I will add later.

In AbstractBaseJWTValidator.validateTokenActive there is a call to token.isActive , which checks also "not before" . Is it sufficient or do we need something more?

I have added the options we have currently in the IdP side (clockSkew and algorithm), but there are other options that we can add: token lifespan (default is 5m=300s) and reusePermitted (default to false). Do we want them or is it OK using fixed values for now?

IMO ok to hardcode "reusePermitted" to false for now. There is the other PR https://github.com/keycloak/keycloak/pull/43841/files , which introduces this including proper config.

For the "token lifespan", I am not sure which token you mean? Lifespan of the assertion is handled by the attributes of JWT itself. Lifespan of the issued token is the same for "jwt-bearer" grant like for any other grants AFAIK.


@Override
public String getClientAssertionSigningAlg() {
return getConfig().getClientAssertionSigningAlg();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively means that the IDP option Client assertion signature algorithm is used for both sending JWT issued by Keycloak for client authentication as well as "expected" algorithm for verification of assertions signed by the client for jwt-bearer grant.

Not 100% sure if this is good thing? Maybe it is OK for most of the cases and it is good to avoid introducing too much options. However for clients, there are different options (EG. ID Token Signature algorithm, User Info Signature algorithm), which is also prescribed in the OIDC specification.

I am like 50/50 whether to use same option or different. If we keep same option, we at least need to update the tooltip IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this was not even added by @graziang, I just added because it was in the base class and makes some sense. We can remove it for the moment or change the tooltip and doc to mention that this is the algorithm to sign any client or authorization grant. Whatever you prefer is OK to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to remove this check for now. If we think we should validate the signing alg for the JWT authorization grant we can add a new prop or change the meaning of the current option. But we can move this forward for the moment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the check and the method in the interface JWTAuthorizationGrantProvider.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good for now. IMO we may need to do this, but likely OK as a follow-up. Thanks!

public BrokeredIdentityContext validateAuthorizationGrantAssertion(JWTAuthorizationGrantValidationContext context) throws IdentityBrokerException {

// verify signature
if (!verify(context.getJws())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you addressed also #43644 with this PR :-) I've assigned that issue to yourself due to that.

It may be also good to remove TODO from line 1079 as I am not sure if there is something more to validate, which we are not already validating? Besides some complex stuff, which we may do with client policies (Follow-up issue for that is #43573 ) .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I did it because using the IdP it was already done. 😄 I will remove the TODO if we accept this way of validating the signature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@rmartinc
Copy link
Contributor Author

@mposolda The lifespan is the 300 that is hardcoded here. The validateTokenActive method uses that time in case the expiration is too long or when the iat is not present to avoid very big times. It's harcoded to 300 now. In clients you can modify that default when you configure JWT to authenticate.

@rmartinc
Copy link
Contributor Author

I have removed the TODO in the OIDCIdentityProvider class and removed the getClientAssertionSigningAlg in the JWTAuthorizationGrantProvider. With this the only remaining question is if we add more configuration methods in the JWT Authorization Grant:

  • The signing algorithm for the JWT authorization grant (same prop or new).
  • If reusing the same token is permitted.
  • The maximum expiration time that is allowed for the grant.

We can manage those as a follow-up if we agree that any of those options are needed too.

@mposolda
Copy link
Contributor

@mposolda The lifespan is the 300 that is hardcoded here. The validateTokenActive method uses that time in case the expiration is too long or when the iat is not present to avoid very big times. It's harcoded to 300 now. In clients you can modify that default when you configure JWT to authenticate.

Ah, ok. Thanks for the clarification!

@mposolda
Copy link
Contributor

I have removed the TODO in the OIDCIdentityProvider class and removed the getClientAssertionSigningAlg in the JWTAuthorizationGrantProvider. With this the only remaining question is if we add more configuration methods in the JWT Authorization Grant:

* The signing algorithm for the JWT authorization grant (same prop or new).

* If reusing the same token is permitted.

* The maximum expiration time that is allowed for the grant.

We can manage those as a follow-up if we agree that any of those options are needed too.

@rmartinc Thanks for the summary. I agree that those should be possible to configure. I've created a follow-up issue #43873 . The "Re-use" is already added in other subsequent PR by Giuseppe for #43568 . So IMO this PR is ready to go.

@mposolda mposolda marked this pull request as ready for review October 31, 2025 08:42
@mposolda mposolda requested a review from a team as a code owner October 31, 2025 08:42
@mposolda
Copy link
Contributor

@rmartinc Could you please rebase? I think that should help with the failing UI tests (not 100% sure, but I guess it might be related to 0f01444 )

@rmartinc
Copy link
Contributor Author

Rebased!

@mposolda mposolda enabled auto-merge (rebase) October 31, 2025 10:26
@mposolda mposolda merged commit f92adda into keycloak:main Oct 31, 2025
132 of 136 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve JWT Assertion Validation

3 participants