Currently, Spring Security only supports basic and post authentication methods between client and authorization server. Would it be possible to add other support for other OpenID authentication methods in a future version of Spring Security, in particular client_secret_jwt and private_key_jwt (see https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)?
Related #6053
Thanks for the report @beuvenar! @jgrandja will take a look at this issue soon
@beuvenar We definitely plan on adding support for other client authentication methods at some point and dependent on user demand. I'm not sure it will get into the next release 5.2. Is there a reason you are requesting client_secret_jwt and private_key_jwt? Do you require this? Which provider are you using that supports these authn methods?
@jgrandja We are currently using Srping Security to implement an integration with OIDC provider of EU Login (formerly ECAS), the authentication service of European Commission. They recommend to use client_secret_jwt or private_key_jwt as authentication methods (but they also support basic method so we are not blocked).
Hi @jgrandja , is there an ETA on this? Which classes should I be looking at to introduce this?
We also need private_key_jwt as a client authentication method in order to implement an integration to the Norwegian ID-porten. We are forced to change client secret often unless we are using private_key_jwt, so I'm looking forward to the 5.3 release.
@phughk Our goal is to provide client_secret_jwt and private_key_jwt client authentication method support in the 5.3.0 release. We could use some help for sure.
The first place to look at is OAuth2AccessTokenResponseClient, which defines the contract for exchanging an authorization grant for an access token. We have implementations DefaultAuthorizationCodeTokenResponseClient, DefaultClientCredentialsTokenResponseClient, DefaultRefreshTokenTokenResponseClient and DefaultPasswordTokenResponseClient. You will notice that each implementation has a member named requestEntityConverter, which can be customized via setRequestEntityConverter(). The Converter is responsible for providing a RequestEntity. This is where the authentication credentials would be set.
You can also check out the reference doc.
Let me know if you have any other questions.
@jgrandja is there any alternative for setRequestEntityConverter in the reactive (ReactiveOAuth2AccessTokenResponseClient) versions of the various OAuth2AccessTokenResponseClients?
Is it instead expected that we add an ExchangeFilterFunction to a WebClient and then provide that WebClient via setWebClient on the chosen ReactiveOAuth2AccessTokenResponseClient?
Even when doing this there does not seem to be a nice way of editing/adding to the body inserter of the original request. I can retrieve the body inserter and key into it, but not retrieve the key first before editing it.
All I want to do is add an extra scope (in addition to the ones already on the OAuth2ClientCredentialsGrantRequest) to the request body and an additional header when making the request to get the token.
@matt-williams8
Is it instead expected that we add an
ExchangeFilterFunctionto aWebClientand then provide thatWebClientviasetWebClienton the chosenReactiveOAuth2AccessTokenResponseClient?
Yes, this is the approach for the reactive side. You can either start with ExchangeFilterFunction.ofRequestProcessor() for request pre-processing or ExchangeFilterFunction.ofResponseProcessor() to post-process the response.
@jgrandja I previously tried that approach, but I hit a blocker.
Unlike the request entity Converter you are able to set on non-reactive OAuth2AccessTokenResponseClients, using an ExchangeFilterFunction on a WebClient does not seem to provide access to the current OAuth2AuthorizationCodeGrantRequest containing the ClientRegistration being used.
I need to be able to add to the scopes being sent as part of the OAuth2 client credentials request. In the ExchangeFilterFunction I can get access to the existing BodyInserter (and cast it to a FormInserter...not a big fan of this either as it relies on knowing that that's what the WebClientReactiveClientCredentialsTokenResponseClient is using under the hood) so that I can edit the body, but by not having access to the OAuth2AuthorizationCodeGrantRequest I cannot ensure that the existing scopes to be used would be maintained. As I cannot fetch the existing value of the scopes attribute from the BodyInserter I would only be able to overwrite the value with the additional scope to be added rather than just adding it on the existing value of the scopes attribute.
Apologies if this is polluting this issue, I'm happy to move the conversation elsewhere.
Hey,
Started working on the private_jwt authentication method as we have a usecase for this.
Hopping to PR this back in fairly soon
@matt-williams8
using an
ExchangeFilterFunctionon aWebClientdoes not seem to provide access to the currentOAuth2AuthorizationCodeGrantRequestcontaining theClientRegistrationbeing used
You can provide your own implementation of ReactiveOAuth2AccessTokenResponseClient that sets the OAuth2AuthorizationCodeGrantRequest as a request attribute:
webClient.post().uri(tokenUri).attribute(OAuth2AuthorizationCodeGrantRequest.class.getName(), authorizationGrantRequest)
Now you can lookup OAuth2AuthorizationCodeGrantRequest using ClientRequest from within the ExchangeFilterFunction. Makes sense?
If you have further questions, please log a new ticket so we can continue on there.
@jgrandja is there interest in the idea of adding the OAuth2AuthorizationCodeGrantRequest as a request attribute part of the official API? The way I'm reading it, the non-reactive implementation (request entity converter) gets this kind of functionality out of the box.
@zpearce I'm not sure I follow you? Can you please provide more detail. As well, if this is not related to this issue please log a new issue and we can continue the convo there.
@jgrandja
private_key_jwt is the preferred authentication method for web apps integrating with login.gov with OpenID Connect. The only other supported method is PKCE which they suggest for native mobile apps.
Previously, I had managed to get the private_key_jwt flow working in Spring Security (non-reactive) by doing a lot of backflips with a custom OAuth2AuthorizationRequestResolver and custom OAuth2AuthorizationCodeGrantRequestEntityConverter.
It would be nice if I could point Spring to my keystore for signing a JWT, and I would no longer be responsible for creating a JWT by deriving information from ClientRegistrationRepository and programmatically adding URL params to the token request.
If anyone wants to reference what I did in a non-reactive app to accomplish this, here you can find my custom resolver, converter, etc.
Similarly to @matt-williams8, I've been trying to translate this flow into a reactive Spring app, and I'm having a lot of issues and could use some help (or this feature :)).
@forgo The most efficient way of getting this feature in is via a PR. We just announced Spring Authorization Server, which will take up quite a bit of resources.
If you or anyone in the community is interested in submitting a PR, I can help guide.
If you or anyone in the community is interested in submitting a PR, I can help guide.
@jgrandja
I've created a branch gh-6881 branch. It's a decent start, but I could use your guidance now.
I signed the "Contributor License Agreement", and I'm following the contributor guidelines, but I have been unable to push my branch. Do I need to do anything else?
@forgo Please submit a Draft Pull Request. Also, please review the contributing section, which provides links on how to submit a PR.
@jgrandja I created the PR. It's definitely a WIP, and I would expect some tests will fail, but I wanted to get what I had out there to start a discussion on if I'm going in the right direction at all.
Much more comments and notes on the PR
Hi @jgrandja ,
I'm taking this over from @Budlee (we work at the same organization). I'm almost done planning out the design for the feature, would you like to review it before I start writing the code? Shall I just put it here, or where can I send it for you to check it out?
Thanks!
@krajcsovszkig-ms I haven't heard from @Budlee in a while and @forgo just submitted a PR #8445. Can you take a look at the PR first and maybe provide feedback there?
Huh, not sure how I didn't notice the latest comments.
I'm happy to get involved with the review and testing, and also to help with the implementation if I can.
Thanks @krajcsovszkig-ms. It would be great if you can take a look at #8445 to provide feedback and share your design ideas. The more eyes on it the better the design outcome will be.
Hi @jgrandja,
We are having a discussion about the design over on the PR, could you take a look and add your comments?
Thanks!
Sure @krajcsovszkig-ms. I will get to it this week for sure.
Great, thanks @jgrandja
Hey there, I also have the requirement for private_key_jwt client authentication method etc.....any idea of the progress of this feature. I'll probably have to implement something custom for my project, but I was just curious.
Thank you
@brandon3123 We are currently working on this but it will be tight to get this into 5.4 since it's being released in Sep. If it doesn't get into 5.4, it will get into next release for sure.
Hi, great you are working on this.
We have Azure AD and would also like jwt auth.
See: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials
Microsoft needs the x5t header also present in the jwt assertion (which is a thumbprint of the private key).
Is this also in the design? Because it is not in de default spec.
If not could you also include this optionally?
@jgrandja
@sander-su Our goal is to implement to spec. With non-standard features, we typically provide hooks to allow for customizations. Please keep track of this issue and feature development to let me know if you require any additional hooks for your requirements with Azure AD.
@jgrandja Thanks for looking into this.
The most generic way to implement this would be having the option to add additional (static) claims to the header of the jwt.
These additional claims could than be specified in the yaml for the client config.
Furthermore, is it in the design to add a JTI to the jwt?
I'd also like the JWT to be as short lived as possible.
So perhaps, 2 profiles, when using JTI's they are generated each time. When not using a JTI the JWT's could be cached for X amount of time.
Btw which issue is best to comment, this one or https://github.com/spring-projects/spring-security/issues/6053?
@sander-su This is the right issue to comment on. When work starts on this feature, we'll consider your feedback.
Most helpful comment
Hey,
Started working on the private_jwt authentication method as we have a usecase for this.
Hopping to PR this back in fairly soon