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

Skip to content

Conversation

@ben95cd
Copy link

@ben95cd ben95cd commented Nov 2, 2023

Implements the jwt-bearer grant type as described in RFC 7523 for utilizing JWTs as authorization grants.

This implementation adds an additional Assertion Grant grant type for clients which enables the client to submit signed JWTs to the token endpoint and receive an access token for the specified user. It also adds a configuration block for OIDC clients where administrators can configure trusted issuer configurations.

When Keycloak receives an assertion grant request from a client, it will verify the signed JWT against the certificate, issuer, and audience specified in the trusted issuer configs and check the client has the 'impersonate' permission for the specified user. If no config successfully validates the token or if the client is not allowed to impersonate the specified user, the request fails.

Closes #24509

@ben95cd ben95cd requested review from a team as code owners November 2, 2023 18:06
@ben95cd ben95cd requested a review from a team November 2, 2023 18:06
@ben95cd ben95cd requested a review from a team as a code owner November 2, 2023 18:06
Copy link
Contributor

@pedroigor pedroigor left a comment

Choose a reason for hiding this comment

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

@ben95cd Thanks, I see now. We can go with this PR as is if we decide to add support for this RFC.

However, it is definitely something we can solve via token exchange. Some of your changes kinda show how they are related.

I'll work on an alternative implementation where we have the same support from token exchange. Perhaps leveraging the SPI you are introducing as is. Will get back to you once I have the changes.

@cypress
Copy link

cypress bot commented Nov 7, 2023

Passing run #10314 ↗︎

0 550 54 0 Flakiness 0

Details:

Merge 96566d9c85373c2424b6ddea731cd569f846ded2 into c2e41b0...
Project: Keycloak Admin UI Commit: e01d6019af ℹ️
Status: Passed Duration: 07:00 💡
Started: Dec 21, 2023 1:09 PM Ended: Dec 21, 2023 1:16 PM

Review all test suite changes for PR #24512 ↗︎

Copy link

@ghost ghost 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

@ghost
Copy link

ghost commented Nov 7, 2023

Unreported flaky test detected

If 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#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore

Keycloak CI - FIPS IT (non-strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

org.keycloak.testsuite.x509.X509BrowserCRLTest#loginSuccessWithCRLSignedWithIntermediateCA3FromTruststore

Keycloak CI - FIPS IT (strict)

java.lang.RuntimeException: Could not create statement
	at org.jboss.arquillian.junit.Arquillian.methodBlock(Arquillian.java:313)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
...

Report flaky test

@pedroigor
Copy link
Contributor

Hey @ben95cd, sorry for the late reply on this one.

I've talked with @stianst and @mposolda about RFC 7523 and the decision is that we should go for it. We did not reach a consensus about how it overlaps with token exchange so having this specification supported is perfectly fine and perhaps the first step to start supporting different token formats as a credential when issuing tokens. In any case, having some baseline for RFC7521 is a nice addition and the SPI herein introduced is a start.

The only thing concern about the changes here is around the place where the trust/cross-domain settings belong to. We think the setting should be at the realm level because:

  • Clients can share the same trust settings
  • These settings can also be useful in other places such as brokering

What is your thought about the concern above? Is it something you think we can address in this PR while still supporting your use cases?

@ben95cd
Copy link
Author

ben95cd commented Nov 28, 2023

Hey @pedroigor no worries at all! Great to hear that you all are interested in implementing RFC 7523.

That is certainly something that can be addressed in this PR, however, to meet our use cases I would want keep have both a realm level and client level config and have the client level config take precedence if it is configured, similar to how other features are configured in Keycloak. I can work on updating that and will follow up again when i have the new changes pushed.

@pedroigor
Copy link
Contributor

@ben95cd I'm wondering if we need it in two places or have a single source truth for cross-domain trust configuration.

For instance, by grouping the settings you are introducing (issuer, audience, certificate) we would then just reference a specific trust configuration to individual clients.

By doing this, I think it is clearer for an administrator what the domains trusted by a realm are and possibly help to identify more easily which clients within a realm are trusting a specific domain.

I can also see in the future more settings being introduced there ...

I know it adds a bit more complexity to this contribution but perhaps it can help to address a lot of other use cases, enhancing/addressing more use cases, including this one.

Wdyt?

@reesing322
Copy link

I have an implementation question on this feature: at the moment, to generate the access token, a transient session is created. However, as this is not a 'real' session, the generated access token cannot be introspected (introspection in keycloak fails when no usersession can be found).
I have tried to run a slightly adapted version of this code, with a persistent session instead of the transient one, and then the RFC7523 feature still works, and an introspection request for the generated access token actually returns data with active=true in it.
Was there a specific reason to use a transient user session, and if there isn't one, shouldn't the implementation be adapted?

@ben95cd
Copy link
Author

ben95cd commented Jun 19, 2024

Hey @PeterSimonis sorry about the delay! Rebased so should be all set!

@reesing322 Using transient sessions was a suggestion by @pedroigor. I think it could make sense for the client to be able to toggle between persistent and transient sessions. Since this one has been sitting for a while, my preference would be to merge this PR and then make that change as another PR. But open to other thoughts.

@reesing322
Copy link

Personally, I would rather have it as soon as possible of course, because the current transient implementation doesn't work for our scenario. But it is not up to me, people like @pedroigor should decide.

…lizing JWTs as authorization grants.

This implementation adds an additional Assertion Grant grant type for clients which enables the client to submit signed JWTs to the token endpoint and receive an access token for the specified user. It also adds a configuration block for OIDC clients where administrators can configure trusted issuer configurations. When Keycloak receives an assertion grant request from a client, it will verify the signed JWT against the certificate, issuer, and audience specified in the trusted issuer configs and check the client has the 'impersonate' permission for the specified user. If no configs successfully validate the token or if the client is not allowed to impersonate the specified user, the request fails.

Closes keycloak#24509

Signed-off-by: Ben Cresitello-Dittmar <[email protected]>
Copy link
Contributor

@pedroigor pedroigor left a comment

Choose a reason for hiding this comment

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

@ben95cd Thanks for the updates and sorry for the delay.

For me, it looks good but I will ask @keycloak/ui-maintainers to give their feedback around the UX/UI. I'm also asking @mposolda and @rmartinc to review it too.

I'll be running the changes to try to find anything else but the only thing that I have to comment, so far, is the fact you are relying on fine-grained admin permissions to check if the client is allowed to impersonate the user.

Perhaps we could also think about using RBAC and the impersonation role to allow the service account to impersonate any user in a realm without being forced to enable/configure fine-grained admin?

@pedroigor
Copy link
Contributor

@ben95cd @reesing322 About transient sessions, I agree we should have this one as is first. We can add support for later for "persistent" sessions.

@reesing322 Doesn't it work for your use case to just issue another token request to obtain a new token rather than use a RT?

TokenVerifier<JsonWebToken> verifier = TokenVerifier.create(assertion, JsonWebToken.class)
.withChecks(
TokenVerifier.IS_ACTIVE,
new TokenVerifier.AudienceCheck(config.getAudience()),
Copy link
Contributor

@pedroigor pedroigor Jul 10, 2024

Choose a reason for hiding this comment

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

I tried to find discussions around the audience constraints but I did not find any.

I would like to know why not allow the token endpoint as a valid audience, even if not defined in the configuration. Perhaps, we could even remove the audience from the configuration and always expect the audience to be the token endpoint?

I think having the token endpoint as a valid audience helps to simplify how assertions are issued because the token endpoint is exposed in the discovery document, making it easier to find the right value to use as the audience and using a standard claim from that document. From a management perspective, it should also help to avoid specifying the value for the audience because the token endpoint is the same across different combinations of issuer and certificate.


* Issuer: The URI identifying this trusted domain. This value will be used to verify the issuer on assertions signed by this trusted domain.
* Audience: The URI identifying the audience of assertions submitted by this trusted domain. This value will be used to verify the audience on assertions signed by this trusted domain.
* Certificate: The base64 encoded X509 signing certificate of the trusted domain. This will be used to verify assertions signed by this trusted domain.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be natural to support jwks urls here

Copy link
Contributor

Choose a reason for hiding this comment

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

Does it have to be ceritificate?
Can it be also public key?

It would be useful in the case when the issuer of assertion is Okta, because Okta looks provide only public key, not certificate for signature verification of the assertion.

@@ -0,0 +1,21 @@
[[cross_domain_trust]]
=== Configuring Cross-Domain Trusts
Copy link
Contributor

Choose a reason for hiding this comment

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

What about identity brokers, would it make sense to somehow link this concept of trusted domains with identity broker providers?

Each cross-domain trust configuration contains 3 elements:

* Issuer: The URI identifying this trusted domain. This value will be used to verify the issuer on assertions signed by this trusted domain.
* Audience: The URI identifying the audience of assertions submitted by this trusted domain. This value will be used to verify the audience on assertions signed by this trusted domain.
Copy link
Contributor

Choose a reason for hiding this comment

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

What if there are multiple audiences, and different client use different audiences?

As an example let's imagine we're setting up a cross-domain trust with Google as the assertion provider. In a realm there are two clients (client-a and client-b), where each client has their own client configured with Google.

domains is used by other features, such as the <<_assertion_grant,JWTs as Authorization Grants>> feature which allows external entities
to request access tokens for users.

To configure a trusted domain:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd imagine there would be a need to be able to lock down things further to have checks on claims within the assertions. For example if a different Keycloak server is the trusted domain, then since Keycloak uses JWTs for refresh, ID, and access tokens, there would need to be some mechanism to for example only permit ID tokens. For other providers there could be other claims that would need to be checked.


The protocol works as follows:

. A trusted entity generates a JWT, specifying the username of the user in the _sub_ claim of the JWT
Copy link
Contributor

Choose a reason for hiding this comment

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

The username from the assertion does not necessarily map directly to a username in Keycloak.


To perform assertion grant requests, the client submitting the assertion grant request must be authorized to impersonate users.

For details on fine grain permissions and the _impersonate_ role, see the <<full-list-of-permissions,Fine grain admin permissions>> chapter.
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume token exchange (and fine grained admin perissions) allows limiting what users can be impersonated? For example a specific group of users.

@stianst stianst requested a review from ahus1 July 22, 2024 09:01
@stianst
Copy link
Contributor

stianst commented Jul 22, 2024

Adding @ahus1 as reviewer, as we need to make this either not use sessions, or consider how it works with durable sessions, multi-site setups, etc.

I could also imagine we'd need some performance testing around this, especially if we actually use sessions, as there could be quite a lot of sessions being created.

@ahus1
Copy link
Contributor

ahus1 commented Jul 23, 2024

Some thoughts on performance testing and persistent sessions: For now this PR is using transient user sessions that live only for a single request. For this, no additional performance testing is needed. So I'm removing myself as a reviewer here.

Some discussions above ask for the token introspection to work, and want persistent sessions to be enabled. I see that the ClientCredentialsGrantType has a switch useRefreshToken which then creates persistent user sessions, and the discussion above seems to go into a similar direction (not sure if useRefreshToken would be a good name for this option here, or if it ever was for ClientCredentialsGrantType).

I don't know how token introspection works for client credentials grants, maybe that would give some insights.
Making user sessions persistent would be a high price for using the token introspection endpoint as it would either bloat the memory, or add database IO each time they are written to the database. To me, this would be a separate PR and discussion as also Pedro suggested above.

@ahus1 ahus1 removed their request for review July 23, 2024 08:02
@ben95cd
Copy link
Author

ben95cd commented Jul 29, 2024

@pedroigor @stianst @ahus1 thanks for all the feedback. Condensing the different comments / asks below to make sure we are in agreement on what I should update/change in order to get this PR through. Could you confirm that sound good before I go ahead and make the changes?

For the transient session question, it seems like we are in agreement that this PR will continue to utilize transient sessions and persistent sessions could be added later on if necessary. So no changes necessary.

@stianst noted about the documentation comments. I will update them accordingly to ensure they are accurate.

I think having the token endpoint as a valid audience helps to simplify how assertions are issued because the token endpoint is exposed in the discovery document, making it easier to find the right value to use as the audience and using a standard claim from that document. From a management perspective, it should also help to avoid specifying the value for the audience because the token endpoint is the same across different combinations of issuer and certificate.

I agree, I think that would simplify the configuration and make it more straight forward as well. I will adjust the behavior accordingly.

Perhaps we could also think about using RBAC and the impersonation role to allow the service account to impersonate any user in a realm without being forced to enable/configure fine-grained admin?

I agree, it would make sense to have it check both the RBAC impersonation role and then the fine-grained admin roles if fine-grained authz is enabled. I will adjust the behavior accordingly.

What if there are multiple audiences, and different client use different audiences?

As an example let's imagine we're setting up a cross-domain trust with Google as the assertion provider. In a realm there are two clients (client-a and client-b), where each client has their own client configured with Google.

Agreed, @pedroigor 's comment above should address this concern by changing the audience from a configurable value to the token endpoint. Additionally adds consistency so the endpoint matches that in the discovery document.

Would be natural to support jwks urls here

What about identity brokers, would it make sense to somehow link this concept of trusted domains with identity broker providers?

The decision to create a cross-domain trust configuration was originally recommended by @pedroigor. The idea was that these cross-domain trust values could eventually be used by other features added in the future. By using JWKs URLs it would restrict these cross-domain trusts to other OIDC providers. My recommendation would be to keep this protocol agnostic for the most flexibility in the future.

When I started developing this feature, I thought about potentially utilizing the identity broker configurations, however, as identity brokers are currently implemented, it would allow federated login to those providers as well which may not be the intended behavior. To prevent that, another option such as "Assertion Grants Only" would need to be added to it. But would require refactoring of the identity broker configurations and I wanted to touch as few other features as possible. So that is why i went down the cross-domain trust route.

setAssertionInUserSessionNotes(jwt, userSession);

AuthenticationSessionModel authSession = createAuthSession(requestedUser);
updateUserSessionFromClientAuth(userSession, clientAuthAttributes);

Choose a reason for hiding this comment

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

Hi Ben,

It looks like the implementation on line 330 could be replaced with a call to the base class method updateUserSessionFromClientAuth(userSession). This might allow us to remove the redundant code.

What do you think?

Thanks!

@pendula95
Copy link

pendula95 commented Sep 27, 2025

Any particular reason why this was staled? I am interested in JWTs as Authorization Grants enabled on Keycloak, I can see that significant work has been already put in to this. Is there interest from the original commit-er @ben95cd to finish this or someone from Keycloak team? I am willing to assist in this.

@bucchi
Copy link
Contributor

bucchi commented Oct 19, 2025

Same here. I also can do something for making this PR to be merged as soon as possible.

I need this because in my little PoC project of SEP-646, Keycloak is used as MCP Authorization Server.

So far, I made build this PR and executed AssertionGrantTest with 26.3.4.
And then I made PoC environment in my PC with Okta trial license and got ID-JAG from the Okta as Idetity provider.

Right now, I am working on making Keycloak process the ID-JAG token and return access token with this PR.
Actually I already have some issue here.
So maybe I can also make some feedbacks to this RP from that experience like this

@mabartos
Copy link
Contributor

What's the progress on this?

@mposolda
Copy link
Contributor

There is a work in progress on the RFC-7523 implementation and it is already implemented as an experimental feature in Keycloak. The idea is to have this preview feature in Keycloak 26.5. The approach uses identity providers as a provider of "trust relationship" rather than introducing some new concept like done in this PR.

I am closing this PR for now in favor of other PR.

@ben95cd Thanks for your work. It is not included in a way you proposed in this PR, but RFC-7523 would be supported soon and we have same intention to actually make RFC-7523 jwt-bearer grant to be officially supported in Keycloak asap.

Anyone is welcome to follow this GH ticket for further details: #43152

@mposolda mposolda closed this Nov 19, 2025
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.

Implement JWT bearer tokens as authorization grants (RFC 7523)