From 719695dda6ba60e5c44d3e11230ad6b7a218442b Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Thu, 29 Aug 2024 19:44:29 +0100 Subject: [PATCH] Add OIDC Client SPI --- bom/application/pom.xml | 5 ++ ...urity-openid-connect-client-reference.adoc | 36 ++++++++++++++ .../deployment/OidcClientBuildStep.java | 6 ++- .../oidc/client/OidcClientResource.java | 10 ++++ .../OidcClientUserPasswordTestCase.java | 11 +++++ extensions/oidc-client/pom.xml | 1 + extensions/oidc-client/runtime/pom.xml | 4 ++ .../client/runtime/TokenProviderProducer.java | 27 +++++++++++ extensions/oidc-client/spi/pom.xml | 47 +++++++++++++++++++ .../oidc/client/spi/TokenProvider.java | 13 +++++ 10 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/TokenProviderProducer.java create mode 100644 extensions/oidc-client/spi/pom.xml create mode 100644 extensions/oidc-client/spi/src/main/java/io/quarkus/oidc/client/spi/TokenProvider.java diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 7ad8ce8a6fbb0..7c776eec56cfa 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -918,6 +918,11 @@ quarkus-oidc-client-deployment ${project.version} + + io.quarkus + quarkus-oidc-client-spi + ${project.version} + io.quarkus quarkus-resteasy-client-oidc-filter diff --git a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc index 2de1e8a175bf7..2a9992bf749f0 100644 --- a/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc +++ b/docs/src/main/asciidoc/security-openid-connect-client-reference.adoc @@ -934,6 +934,42 @@ quarkus.oidc-client.tls.trust-store-password=${trust-store-password} #quarkus.oidc-client.tls.trust-store-alias=certAlias ---- +=== OIDC Client SPI + +When your custom extension must acquire OIDC tokens using one of the OIDC token grants supported by OIDC client, this extension can depend on the OIDC Client SPI only and let OIDC client itself acquire and refresh access tokens as necessary. + +Add the following dependency: + +[source,xml] +---- + + io.quarkus + quarkus-oidc-client-spi + +---- + +Next update your extension to use `io.quarkus.oidc.client.spi.TokenProvider` CDI bean as required, for example: + +[source,java] +---- +package org.acme.extension; + +import jakarta.inject.Inject; +import io.quarkus.oidc.client.spi.TokenProvider; + +public class ExtensionOAuth2Support { + + @Inject + TokenProvider tokenProvider; + + public Uni getAccessToken() { + return tokenProvider.getAccessToken(); + } +} +---- + +Currently, `io.quarkus.oidc.client.spi.TokenProvider` is only available for default OIDC clients, since custom extensions are unlikely to be aware of multiple named OIDC clients. + [[integration-testing-oidc-client]] === Testing diff --git a/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java b/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java index f9d10a3304f1a..fe6e9fae6f74f 100644 --- a/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java +++ b/extensions/oidc-client/deployment/src/main/java/io/quarkus/oidc/client/deployment/OidcClientBuildStep.java @@ -48,6 +48,7 @@ import io.quarkus.oidc.client.runtime.OidcClientBuildTimeConfig; import io.quarkus.oidc.client.runtime.OidcClientRecorder; import io.quarkus.oidc.client.runtime.OidcClientsConfig; +import io.quarkus.oidc.client.runtime.TokenProviderProducer; import io.quarkus.oidc.client.runtime.TokensHelper; import io.quarkus.oidc.client.runtime.TokensProducer; import io.quarkus.oidc.token.propagation.AccessToken; @@ -66,7 +67,10 @@ ExtensionSslNativeSupportBuildItem enableSslInNative() { @BuildStep void registerProvider(BuildProducer additionalBeans) { - additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(TokensProducer.class)); + AdditionalBeanBuildItem.Builder builder = AdditionalBeanBuildItem.builder().setUnremovable(); + builder.addBeanClass(TokensProducer.class); + builder.addBeanClass(TokenProviderProducer.class); + additionalBeans.produce(builder.build()); } @BuildStep diff --git a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientResource.java b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientResource.java index bad7734d50387..85ee90b494294 100644 --- a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientResource.java +++ b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientResource.java @@ -6,6 +6,7 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; +import io.quarkus.oidc.client.spi.TokenProvider; import io.smallrye.mutiny.Uni; @Path("/client") @@ -14,6 +15,9 @@ public class OidcClientResource { @Inject OidcClient client; + @Inject + TokenProvider tokenProvider; + @Inject @NamedOidcClient("key") OidcClient keyClient; @@ -30,6 +34,12 @@ public Uni tokenUni() { return client.getTokens().flatMap(tokens -> Uni.createFrom().item(tokens.getAccessToken())); } + @GET + @Path("tokenprovider") + public Uni tokenProvider() { + return tokenProvider.getAccessToken(); + } + @GET @Path("tokens") public Uni grantTokensUni() { diff --git a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientUserPasswordTestCase.java b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientUserPasswordTestCase.java index 388007bee0e6d..aea7a9395336a 100644 --- a/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientUserPasswordTestCase.java +++ b/extensions/oidc-client/deployment/src/test/java/io/quarkus/oidc/client/OidcClientUserPasswordTestCase.java @@ -44,6 +44,17 @@ public void testPasswordGrantToken() { } + @Test + public void testPasswordGrantTokenProvider() { + String token = RestAssured.when().get("/client/tokenprovider").body().asString(); + RestAssured.given().auth().oauth2(token) + .when().get("/protected") + .then() + .statusCode(200) + .body(equalTo("alice")); + + } + @Test public void testPublicClientPasswordGrantToken() { String token = RestAssured.when().get("/public-client/token").body().asString(); diff --git a/extensions/oidc-client/pom.xml b/extensions/oidc-client/pom.xml index 58792529ef294..f5601a086a7cc 100644 --- a/extensions/oidc-client/pom.xml +++ b/extensions/oidc-client/pom.xml @@ -16,5 +16,6 @@ deployment runtime + spi diff --git a/extensions/oidc-client/runtime/pom.xml b/extensions/oidc-client/runtime/pom.xml index 35afc5fbf4ebf..502dc746c7c83 100644 --- a/extensions/oidc-client/runtime/pom.xml +++ b/extensions/oidc-client/runtime/pom.xml @@ -25,6 +25,10 @@ io.quarkus quarkus-oidc-common + + io.quarkus + quarkus-oidc-client-spi + io.quarkus quarkus-junit5-internal diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/TokenProviderProducer.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/TokenProviderProducer.java new file mode 100644 index 0000000000000..65fd914a26313 --- /dev/null +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/TokenProviderProducer.java @@ -0,0 +1,27 @@ +package io.quarkus.oidc.client.runtime; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Singleton; + +import io.quarkus.oidc.client.spi.TokenProvider; +import io.smallrye.mutiny.Uni; + +@Singleton +public class TokenProviderProducer extends AbstractTokensProducer { + + @Produces + @RequestScoped + public TokenProvider produceTokenProvider() { + return new TokenProviderImpl(); + } + + class TokenProviderImpl implements TokenProvider { + + @Override + public Uni getAccessToken() { + return TokenProviderProducer.super.getTokens().onItem().transform(t -> t.getAccessToken()); + } + + }; +} diff --git a/extensions/oidc-client/spi/pom.xml b/extensions/oidc-client/spi/pom.xml new file mode 100644 index 0000000000000..d5e7da4b77b55 --- /dev/null +++ b/extensions/oidc-client/spi/pom.xml @@ -0,0 +1,47 @@ + + + + quarkus-oidc-client-parent + io.quarkus + 999-SNAPSHOT + + 4.0.0 + + quarkus-oidc-client-spi + Quarkus - OIDC Client - SPI + + + + io.quarkus + quarkus-core + + + io.smallrye.reactive + mutiny + + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + + diff --git a/extensions/oidc-client/spi/src/main/java/io/quarkus/oidc/client/spi/TokenProvider.java b/extensions/oidc-client/spi/src/main/java/io/quarkus/oidc/client/spi/TokenProvider.java new file mode 100644 index 0000000000000..3f7d9bfdb2556 --- /dev/null +++ b/extensions/oidc-client/spi/src/main/java/io/quarkus/oidc/client/spi/TokenProvider.java @@ -0,0 +1,13 @@ +package io.quarkus.oidc.client.spi; + +import io.smallrye.mutiny.Uni; + +public interface TokenProvider { + + /** + * Get a valid, if necessary refreshed, access token. + * + * @return the access token + */ + Uni getAccessToken(); +}