Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance OidcClientFilter to select named OIDC clients #16397

Closed
sberyozkin opened this issue Apr 9, 2021 · 14 comments · Fixed by #29788
Closed

Enhance OidcClientFilter to select named OIDC clients #16397

sberyozkin opened this issue Apr 9, 2021 · 14 comments · Fixed by #29788
Milestone

Comments

@sberyozkin
Copy link
Member

@fwippe has noticed that OidcClientFilter only works with the default OidcClient.

It would be useful to have something like this:

@OidcClientFilter("my-client")
MyMpRestClient client. 

If this property is set then the filter implementation would just have to be updated to do OidcClients.getClient("my-client") - what is not clear though is how OidcClientFilter can find out about the client id it needs to use.

@quarkus-bot
Copy link

quarkus-bot bot commented Apr 9, 2021

/cc @pedroigor

@sberyozkin
Copy link
Member Author

May be OidcClientFilter configuration can have a client-id property - and we'd register an already initialized object as a resteasy provider as opposed to a class; I'll have a look

@sberyozkin sberyozkin removed their assignment Apr 9, 2021
@sberyozkin
Copy link
Member Author

sberyozkin commented Apr 9, 2021

That said, a custom filter would do quite well too; so OidcClientFilter("myclient") would be nice - but the trick is to make it visible to OidcClientFilter implementation in its post-construct time

@sberyozkin
Copy link
Member Author

I think what we can do, is at the point of registering the provider with RestEasy, we can check if the constructor accepting the activating annotation is available, for example, OidcClientRequestFilter(OidcClient) where we can check the annotation properties.

@fwippe
Copy link
Contributor

fwippe commented Apr 10, 2021

I think what we can do, is at the point of registering the provider with RestEasy, we can check if the constructor accepting the activating annotation is available, for example, OidcClientRequestFilter(OidcClient) where we can check the annotation properties.

I like the idea of introducing a new optional annotation value clientName for annotation @OidcClientFilter. In a build step one could:

  1. discover the actually used annotations @OidcClientFilter with clientName != default
  2. synthesize corresponding annotations @OidcClientFilter_clientName
  3. synthesize corresponding filters OidcClientRequestFilter_clientName
  4. register annotations of 2. and 3. via RestClientAnnotationProviderBuildItem
  5. transform annotations @OidcClientFilter with clientName != default to the new annotation of 2. via an AnnotationTransformer

But maybe that's a little bit too much on the black bytecode magic side?

@sberyozkin
Copy link
Member Author

@fwippe yes, I agree :-), I'm hoping the fix will be simpler such that it can also work generically, I'll have a look shortly

@sberyozkin
Copy link
Member Author

sberyozkin commented Apr 13, 2021

@fwippe While you are looking into the named clients, here is a more concrete proposal:

I believe what should be passed is a holder of (provider Class + the matching Annotation instance), example, we can have a runtime/ProviderInput or something similar to hold both values.

Then, in RestClientBase - when this provider classes are registered - each class is checked if it has a constructor accepting this annotation's class such as OidcClientRequestFilter(OidcClientFilter) - if yes - then we create an instance and register it as a provider, if not - register class as a provider (as usual). Then OidcClientRequestFilter will return the client id in clientId() override.

This way we can support a generic feature where the providers can be totally configured via the enabling annotations - and it will work not only for OidcClientFilter

Please investigate

@fwippe
Copy link
Contributor

fwippe commented Apr 15, 2021

@sberyozkin

@fwippe While you are looking into the named clients, here is a more concrete proposal:

I believe what should be passed is a holder of (provider Class + the matching Annotation instance), example, we can have a runtime/ProviderInput or something similar to hold both values.

Then, in RestClientBase - when this provider classes are registered - each class is checked if it has a constructor accepting this annotation's class such as OidcClientRequestFilter(OidcClientFilter) - if yes - then we create an instance and register it as a provider, if not - register class as a provider (as usual). Then OidcClientRequestFilter will return the client id in clientId() override.

Sounds like a reasonable approach! But there's a serious drawback: We're no longer talking about CDI beans here, are we? As we're creating the instance ourselves, no injections and scopes will be possible, right? Is that a price you're willing to pay?

@sberyozkin
Copy link
Member Author

sberyozkin commented Apr 15, 2021

@fwippe Good point. well, if they are passed directly as classes to RestEasy - how does it create them for the injection to work ?

CC @ronsigal hi Ron - can you clarify please ?

@fwippe
Copy link
Contributor

fwippe commented Apr 15, 2021

@sberyozkin

@fwippe Good point. well, if they are passed directly as classes to RestEasy - how does it create them for the injection to work ?

My best guess is org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.injectedInstance(Class<? extends T>). It will involve picking the constructor with most parameters, though. I'm not sure how to hook into that construction process yet.

@sberyozkin
Copy link
Member Author

sberyozkin commented Apr 15, 2021

@fwippe Sure - lets assume the provider has only a default constructor - what I'm curious about, does Resteasy do anything special to ensure the field injection (@Inject Tokens t; etc) into this provider works, how does it create an instance, once it finds the best match constructror

@fwippe
Copy link
Contributor

fwippe commented Apr 15, 2021

@sberyozkin The injection mechanism is abstracted in multiple layers. I guess it boils down to using the Arc-BeanContainer in io.quarkus.resteasy.common.runtime.QuarkusInjectorFactory.

@sberyozkin
Copy link
Member Author

@fwippe the providers registered as classes are submitted directly into MP RestClient's RestClientBuilder. And yet the injection into these providers works. So it may well work with the instances as well. I'll experiment

@mickroll
Copy link
Contributor

mickroll commented May 20, 2022

Workaround:
starting point:

@Path(BASEPATH)
@RegisterRestClient(configKey = "my-connector")
@OidcClientFilter
public interface KeycloakUserApi [...]

solution:

  • create custom OidcClientRequestFilter that supplies the correct client-id:
public class ExampleOidcClientRequestFilter extends OidcClientRequestFilter {
    @Override
    protected Optional<String> clientId() {
        return Optional.of("example-client-id"); // has to match configured oidc-client id
    }
}
  • use it on interface:
@Path(BASEPATH)
@RegisterRestClient(configKey = "my-connector")
@RegisterProvider(ExampleOidcClientRequestFilter.class)
public interface KeycloakUserApi [...]
  • with example config:
quarkus:
  oidc-client:
    example-client-id:
      auth-server-url: [...]

michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 11, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 11, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 11, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 11, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 11, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 12, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
michalvavrik added a commit to michalvavrik/quarkus that referenced this issue Dec 14, 2022
@quarkus-bot quarkus-bot bot added this to the 2.16 - main milestone Dec 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants