diff --git a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java
index 6538f9ce4..fa9e3faa9 100644
--- a/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java
+++ b/client/deployment/src/main/java/io/quarkiverse/openapi/generator/deployment/GeneratorProcessor.java
@@ -174,6 +174,7 @@ void produceOauthAuthentication(CombinedIndexBuildItem beanArchiveBuildItem,
.annotation(OpenApiSpec.class)
.addValue("openApiSpecId", openApiSpecId)
.done()
+ .addInjectionPoint(ClassType.create(DotName.createSimple(CredentialsProvider.class)))
.addInjectionPoint(ClassType.create(OAuth2AuthenticationProvider.OidcClientRequestFilterDelegate.class),
AnnotationInstance.builder(OidcClient.class).add("name", sanitizeAuthName(name)).build())
.addInjectionPoint(ClassType.create(DotName.createSimple(CredentialsProvider.class)))
diff --git a/client/integration-tests/auth-provider/pom.xml b/client/integration-tests/auth-provider/pom.xml
new file mode 100644
index 000000000..8a0bddcc2
--- /dev/null
+++ b/client/integration-tests/auth-provider/pom.xml
@@ -0,0 +1,124 @@
+
+
+
+ quarkus-openapi-generator-integration-tests
+ io.quarkiverse.openapi.generator
+ 3.0.0-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-openapi-generator-it-auth-provider
+ Quarkus - OpenAPI Generator - Integration Tests - Client - Auth Provider
+ A few use cases that relies on authentication provider use cases with the OpenAPI Generator
+
+
+
+ io.quarkiverse.openapi.generator
+ quarkus-openapi-generator
+
+
+ io.quarkiverse.openapi.generator
+ quarkus-openapi-generator-oidc
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ org.wiremock
+ wiremock
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+
+
+
+ native-image
+
+
+ native
+
+
+
+
+
+ maven-surefire-plugin
+
+ ${native.surefire.skip}
+
+
+
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+ native
+
+
+
+ resteasy-reactive
+
+
+ io.quarkus
+ quarkus-rest-client-oidc-filter
+
+
+
+
+ resteasy-classic
+
+ true
+
+
+
+ io.quarkus
+ quarkus-resteasy-client-oidc-filter
+
+
+ io.quarkus
+ quarkus-resteasy-multipart
+
+
+
+
+
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/TokenServerResource.java b/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/TokenServerResource.java
new file mode 100644
index 000000000..9c3ae7e57
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/TokenServerResource.java
@@ -0,0 +1,50 @@
+package io.quarkiverse.openapi.generator.it.auth;
+
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+
+import org.eclipse.microprofile.rest.client.inject.RestClient;
+
+@Path("/token_server")
+public class TokenServerResource {
+
+ @RestClient
+ org.acme.externalservice1.api.DefaultApi defaultApi1;
+
+ @RestClient
+ org.acme.externalservice2.api.DefaultApi defaultApi2;
+
+ @RestClient
+ org.acme.externalservice3.api.DefaultApi defaultApi3;
+
+ @RestClient
+ org.acme.externalservice5.api.DefaultApi defaultApi5;
+
+ @POST
+ @Path("service1")
+ public String service1() {
+ defaultApi1.executeQuery1();
+ return "hello";
+ }
+
+ @POST
+ @Path("service2")
+ public String service2() {
+ defaultApi2.executeQuery2();
+ return "hello";
+ }
+
+ @POST
+ @Path("service3")
+ public String service3() {
+ defaultApi3.executeQuery3();
+ return "hello";
+ }
+
+ @POST
+ @Path("service5")
+ public String service5() {
+ defaultApi5.executeQuery5();
+ return "hello";
+ }
+}
diff --git a/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/provider/CustomCredentialsProvider.java b/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/provider/CustomCredentialsProvider.java
new file mode 100644
index 000000000..e2bc1b8af
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/java/io/quarkiverse/openapi/generator/it/auth/provider/CustomCredentialsProvider.java
@@ -0,0 +1,27 @@
+package io.quarkiverse.openapi.generator.it.auth.provider;
+
+import jakarta.annotation.Priority;
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.inject.Alternative;
+import jakarta.enterprise.inject.Specializes;
+
+import io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider;
+
+@Dependent
+@Alternative
+@Specializes
+@Priority(200)
+public class CustomCredentialsProvider extends ConfigCredentialsProvider {
+ public CustomCredentialsProvider() {
+ }
+
+ @Override
+ public String getBearerToken(CredentialsContext input) {
+ return super.getBearerToken(input) + "_TEST";
+ }
+
+ @Override
+ public String getOauth2BearerToken(CredentialsContext input) {
+ return super.getOauth2BearerToken(input) + "_TEST";
+ }
+}
diff --git a/client/integration-tests/auth-provider/src/main/openapi/token-external-service1.yaml b/client/integration-tests/auth-provider/src/main/openapi/token-external-service1.yaml
new file mode 100644
index 000000000..dd18964c1
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/openapi/token-external-service1.yaml
@@ -0,0 +1,19 @@
+---
+openapi: 3.0.3
+info:
+ title: token-external-service1 API
+ version: 3.0.0-SNAPSHOT
+paths:
+ /token-external-service1/executeQuery1:
+ post:
+ operationId: executeQuery1
+ responses:
+ "200":
+ description: OK
+ security:
+ - service1-http-bearer: []
+components:
+ securitySchemes:
+ service1-http-bearer:
+ type: http
+ scheme: bearer
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/main/openapi/token-external-service2.yaml b/client/integration-tests/auth-provider/src/main/openapi/token-external-service2.yaml
new file mode 100644
index 000000000..726200831
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/openapi/token-external-service2.yaml
@@ -0,0 +1,23 @@
+---
+openapi: 3.0.3
+info:
+ title: token-external-service2 API
+ version: 3.0.0-SNAPSHOT
+paths:
+ /token-external-service2/executeQuery2:
+ post:
+ operationId: executeQuery2
+ responses:
+ "200":
+ description: OK
+ security:
+ - service2-oauth2: []
+components:
+ securitySchemes:
+ service2-oauth2:
+ type: oauth2
+ flows:
+ clientCredentials:
+ authorizationUrl: https://example.com/oauth
+ tokenUrl: https://example.com/oauth/token
+ scopes: {}
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/main/openapi/token-external-service3.yaml b/client/integration-tests/auth-provider/src/main/openapi/token-external-service3.yaml
new file mode 100644
index 000000000..981daef3c
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/openapi/token-external-service3.yaml
@@ -0,0 +1,19 @@
+---
+openapi: 3.0.3
+info:
+ title: token-external-service3 API
+ version: 3.0.0-SNAPSHOT
+paths:
+ /token-external-service3/executeQuery3:
+ post:
+ operationId: executeQuery3
+ responses:
+ "200":
+ description: OK
+ security:
+ - service3-http-bearer: []
+components:
+ securitySchemes:
+ service3-http-bearer:
+ type: http
+ scheme: bearer
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/main/openapi/token-external-service5.yaml b/client/integration-tests/auth-provider/src/main/openapi/token-external-service5.yaml
new file mode 100644
index 000000000..4490dcf00
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/openapi/token-external-service5.yaml
@@ -0,0 +1,23 @@
+---
+openapi: 3.0.3
+info:
+ title: token-external-service5 API
+ version: 3.0.0-SNAPSHOT
+paths:
+ /token-external-service5/executeQuery5:
+ post:
+ operationId: executeQuery5
+ responses:
+ "200":
+ description: OK
+ security:
+ - service5-oauth2: []
+components:
+ securitySchemes:
+ service5-oauth2:
+ type: oauth2
+ flows:
+ clientCredentials:
+ authorizationUrl: https://example.com/oauth
+ tokenUrl: https://example.com/oauth/token
+ scopes: {}
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/main/resources/application.properties b/client/integration-tests/auth-provider/src/main/resources/application.properties
new file mode 100644
index 000000000..eeed16b8a
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/main/resources/application.properties
@@ -0,0 +1,42 @@
+# Note: The property value is the name of an existing securityScheme in the spec file
+quarkus.openapi-generator.codegen.default-security-scheme=app_id
+
+#Token service
+quarkus.openapi-generator.codegen.spec.token_external_service1_yaml.base-package=org.acme.externalservice1
+quarkus.openapi-generator.codegen.spec.token_external_service2_yaml.base-package=org.acme.externalservice2
+quarkus.openapi-generator.codegen.spec.token_external_service3_yaml.base-package=org.acme.externalservice3
+quarkus.openapi-generator.codegen.spec.token_external_service5_yaml.base-package=org.acme.externalservice5
+
+quarkus.rest-client.token_external_service1_yaml.url=${propagation-external-service-mock.url}
+quarkus.rest-client.token_external_service2_yaml.url=${propagation-external-service-mock.url}
+quarkus.rest-client.token_external_service3_yaml.url=${propagation-external-service-mock.url}
+quarkus.rest-client.token_external_service5_yaml.url=${propagation-external-service-mock.url}
+
+# default propagation for token_external_service1 invocation
+quarkus.openapi-generator.token_external_service1_yaml.auth.service1_http_bearer.token-propagation=true
+# default propagation for token_external_service2 invocation
+quarkus.openapi-generator.token_external_service2_yaml.auth.service2_oauth2.token-propagation=true
+
+quarkus.openapi-generator.token_external_service3_yaml.auth.service3_http_bearer.bearer-token=BEARER_TOKEN
+
+# Oidc clients for the services that has oauth2 security.
+# Oidc client used by the token_external_service2
+quarkus.oidc-client.service2_oauth2.auth-server-url=${keycloak.mock.service.url}
+quarkus.oidc-client.service2_oauth2.token-path=${keycloak.mock.service.token-path}
+quarkus.oidc-client.service2_oauth2.discovery-enabled=false
+quarkus.oidc-client.service2_oauth2.client-id=kogito-app
+quarkus.oidc-client.service2_oauth2.grant.type=client
+quarkus.oidc-client.service2_oauth2.credentials.client-secret.method=basic
+quarkus.oidc-client.service2_oauth2.credentials.client-secret.value=secret
+
+
+# Oidc client used by the token_external_service5
+quarkus.oidc-client.service5_oauth2.auth-server-url=${keycloak.mock.service.url}
+quarkus.oidc-client.service5_oauth2.token-path=${keycloak.mock.service.token-path}
+quarkus.oidc-client.service5_oauth2.discovery-enabled=false
+quarkus.oidc-client.service5_oauth2.client-id=kogito-app
+quarkus.oidc-client.service5_oauth2.grant.type=client
+quarkus.oidc-client.service5_oauth2.credentials.client-secret.method=basic
+quarkus.oidc-client.service5_oauth2.credentials.client-secret.value=secret
+
+quarkus.keycloak.devservices.enabled=false
\ No newline at end of file
diff --git a/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/KeycloakServiceMock.java b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/KeycloakServiceMock.java
new file mode 100644
index 000000000..477d7d253
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/KeycloakServiceMock.java
@@ -0,0 +1,82 @@
+package io.quarkiverse.openapi.generator.it.auth;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.configureFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+
+/**
+ * Lightweight Keycloak mock to use when an OidcClient is required, and we don't want/need to start a full Keycloak
+ * container as part of the tests, etc. Keep the things simple.
+ */
+public class KeycloakServiceMock implements QuarkusTestResourceLifecycleManager {
+
+ public static final String KEY_CLOAK_SERVICE_URL = "keycloak.mock.service.url";
+ public static final String KEY_CLOAK_SERVICE_TOKEN_PATH = "keycloak.mock.service.token-path";
+ public static final String REALM = "kogito-tests";
+ public static final String KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE = "/realms/" + REALM + "/protocol/openid-connect/token";
+ public static final String CLIENT_ID = "kogito-app";
+ public static final String SECRET = "secret";
+ public static final String KEYCLOAK_ACCESS_TOKEN = "KEYCLOAK_ACCESS_TOKEN";
+ public static final String KEYCLOAK_REFRESH_TOKEN = "KEYCLOAK_REFRESH_TOKEN";
+ public static final String KEYCLOAK_SESSION_STATE = "KEYCLOAK_SESSION_STATE";
+
+ public static final String AUTH_REQUEST_BODY = "grant_type=client_credentials";
+
+ private static final ThreadLocal wireMockServer = new ThreadLocal<>();
+
+ @Override
+ public Map start() {
+ wireMockServer.set(new WireMockServer(options().dynamicPort()));
+ wireMockServer.get().start();
+ configureFor(wireMockServer.get().port());
+
+ stubFor(post(KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE)
+ .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FORM_URLENCODED))
+ .withBasicAuth(CLIENT_ID, SECRET)
+ .withRequestBody(equalTo(AUTH_REQUEST_BODY))
+ .willReturn(aResponse()
+ .withHeader(CONTENT_TYPE, APPLICATION_JSON)
+ .withBody(getTokenResult())));
+
+ Map properties = new HashMap<>();
+ properties.put(KEY_CLOAK_SERVICE_URL, wireMockServer.get().baseUrl());
+ properties.put(KEY_CLOAK_SERVICE_TOKEN_PATH, KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE);
+ return properties;
+ }
+
+ private static String getTokenResult() {
+ return """
+ {
+ "access_token": "%s",
+ "expires_in": 300,
+ "refresh_expires_in": 1800,
+ "refresh_token": "%s",
+ "token_type": "bearer",
+ "not-before-policy": 0,
+ "session_state": "%s",
+ "scope": "email profile"
+ }
+ """.formatted(KEYCLOAK_ACCESS_TOKEN, KEYCLOAK_REFRESH_TOKEN, KEYCLOAK_SESSION_STATE);
+ }
+
+ @Override
+ public void stop() {
+ if (wireMockServer.get() != null) {
+ wireMockServer.get().stop();
+ wireMockServer.remove();
+ }
+ }
+}
diff --git a/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenExternalServicesMock.java b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenExternalServicesMock.java
new file mode 100644
index 000000000..7a4545218
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenExternalServicesMock.java
@@ -0,0 +1,72 @@
+package io.quarkiverse.openapi.generator.it.auth;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.configureFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static io.quarkiverse.openapi.generator.it.auth.KeycloakServiceMock.KEYCLOAK_ACCESS_TOKEN;
+import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE;
+import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.util.Map;
+
+import jakarta.ws.rs.core.HttpHeaders;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+
+public class TokenExternalServicesMock implements QuarkusTestResourceLifecycleManager {
+
+ public static final String AUTHORIZATION_TOKEN = "AUTHORIZATION_TOKEN";
+ public static final String SERVICE3_AUTHORIZATION_TOKEN = "BEARER_TOKEN";
+ public static final String TOKEN_EXTERNAL_SERVICE_MOCK_URL = "propagation-external-service-mock.url";
+ private static final String BEARER = "Bearer ";
+ private static final Logger LOGGER = LoggerFactory.getLogger(TokenExternalServicesMock.class);
+ private WireMockServer wireMockServer;
+
+ private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) {
+ stubFor(post(tokenPropagationExternalServiceUrl)
+ .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken))
+ .willReturn(aResponse()
+ .withHeader(CONTENT_TYPE, APPLICATION_JSON)
+ .withBody("{}")));
+ }
+
+ @Override
+ public Map start() {
+ wireMockServer = new WireMockServer(options().dynamicPort());
+ wireMockServer.start();
+ configureFor(wireMockServer.port());
+ LOGGER.info("Mocked Server started at {}", wireMockServer.baseUrl());
+
+ // stub the token-external-service1 invocation with the expected token
+ stubForExternalService("/token-external-service1/executeQuery1", AUTHORIZATION_TOKEN);
+
+ // stub the token-external-service2 invocation with the expected token
+ stubForExternalService("/token-external-service2/executeQuery2", AUTHORIZATION_TOKEN);
+
+ // stub the token-external-service3 invocation with the expected token taken from the
+ // application.properties and overridden by the custom credential provider
+ stubForExternalService("/token-external-service3/executeQuery3", SERVICE3_AUTHORIZATION_TOKEN + "_TEST");
+
+ // stub the token-external-service5 invocation with the expected token, no propagation is produced
+ // in this case but the service must receive the token provided by Keycloak since it has oauth2 security
+ // configured. The token will be overridden by the custom credential provider
+ stubForExternalService("/token-external-service5/executeQuery5", KEYCLOAK_ACCESS_TOKEN + "_TEST");
+
+ return Map.of(TOKEN_EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl());
+ }
+
+ @Override
+ public void stop() {
+ if (wireMockServer != null) {
+ wireMockServer.stop();
+ }
+ }
+}
diff --git a/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenWithCustomCredentialProviderTest.java b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenWithCustomCredentialProviderTest.java
new file mode 100644
index 000000000..9fd525475
--- /dev/null
+++ b/client/integration-tests/auth-provider/src/test/java/io/quarkiverse/openapi/generator/it/auth/TokenWithCustomCredentialProviderTest.java
@@ -0,0 +1,35 @@
+package io.quarkiverse.openapi.generator.it.auth;
+
+import static io.quarkiverse.openapi.generator.it.auth.TokenExternalServicesMock.AUTHORIZATION_TOKEN;
+import static io.restassured.RestAssured.given;
+
+import java.util.Map;
+
+import jakarta.ws.rs.core.HttpHeaders;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTestResource(TokenExternalServicesMock.class)
+@QuarkusTestResource(KeycloakServiceMock.class)
+@QuarkusTest
+// Enabled only for RESTEasy Classic while https://github.com/quarkiverse/quarkus-openapi-generator/issues/434 is not fixed
+@Tag("resteasy-classic")
+class TokenWithCustomCredentialProviderTest {
+
+ @ParameterizedTest
+ @ValueSource(strings = { "service1", "service2", "service3", "service5" })
+ void testService(String service) {
+ Map headers = Map.of(HttpHeaders.AUTHORIZATION, AUTHORIZATION_TOKEN);
+
+ given()
+ .headers(headers)
+ .post("/token_server/" + service)
+ .then()
+ .statusCode(200);
+ }
+}
diff --git a/client/integration-tests/override-credential-provider/src/main/java/io/quarkiverse/openapi/generator/it/creds/CustomCredentialsProvider.java b/client/integration-tests/override-credential-provider/src/main/java/io/quarkiverse/openapi/generator/it/creds/CustomCredentialsProvider.java
index 0ac9e2282..8bbc39b68 100644
--- a/client/integration-tests/override-credential-provider/src/main/java/io/quarkiverse/openapi/generator/it/creds/CustomCredentialsProvider.java
+++ b/client/integration-tests/override-credential-provider/src/main/java/io/quarkiverse/openapi/generator/it/creds/CustomCredentialsProvider.java
@@ -3,7 +3,6 @@
import jakarta.annotation.Priority;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Alternative;
-import jakarta.ws.rs.client.ClientRequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,7 +18,7 @@ public class CustomCredentialsProvider extends ConfigCredentialsProvider {
public static String TOKEN = "FIXED_TEST_TOKEN";
@Override
- public String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ public String getBearerToken(CredentialsContext input) {
LOGGER.info("========> getBearerToken from CustomCredentialsProvider");
return TOKEN;
}
diff --git a/client/integration-tests/pom.xml b/client/integration-tests/pom.xml
index fa71bf7f8..2a5f0cd0e 100644
--- a/client/integration-tests/pom.xml
+++ b/client/integration-tests/pom.xml
@@ -12,6 +12,7 @@
pom
additional-properties
+ auth-provider
array-enum
bean-validation
beanparam
diff --git a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/KeycloakServiceMock.java b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/KeycloakServiceMock.java
index 06fd6aa57..264b34bea 100644
--- a/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/KeycloakServiceMock.java
+++ b/client/integration-tests/security/src/test/java/io/quarkiverse/openapi/generator/it/security/KeycloakServiceMock.java
@@ -35,13 +35,13 @@ public class KeycloakServiceMock implements QuarkusTestResourceLifecycleManager
public static final String AUTH_REQUEST_BODY = "grant_type=client_credentials";
- private WireMockServer wireMockServer;
+ private static final ThreadLocal wireMockServer = new ThreadLocal<>();
@Override
public Map start() {
- wireMockServer = new WireMockServer(options().dynamicPort());
- wireMockServer.start();
- configureFor(wireMockServer.port());
+ wireMockServer.set(new WireMockServer(options().dynamicPort()));
+ wireMockServer.get().start();
+ configureFor(wireMockServer.get().port());
stubFor(post(KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE)
.withHeader(CONTENT_TYPE, equalTo(APPLICATION_FORM_URLENCODED))
@@ -52,28 +52,31 @@ public Map start() {
.withBody(getTokenResult())));
Map properties = new HashMap<>();
- properties.put(KEY_CLOAK_SERVICE_URL, wireMockServer.baseUrl());
+ properties.put(KEY_CLOAK_SERVICE_URL, wireMockServer.get().baseUrl());
properties.put(KEY_CLOAK_SERVICE_TOKEN_PATH, KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE);
return properties;
}
private static String getTokenResult() {
- return "{\n" +
- " \"access_token\": \"" + KEYCLOAK_ACCESS_TOKEN + "\",\n" +
- " \"expires_in\": 300,\n" +
- " \"refresh_expires_in\": 1800,\n" +
- " \"refresh_token\": \"" + KEYCLOAK_REFRESH_TOKEN + "\",\n" +
- " \"token_type\": \"bearer\",\n" +
- " \"not-before-policy\": 0,\n" +
- " \"session_state\": \"" + KEYCLOAK_SESSION_STATE + "\",\n" +
- " \"scope\": \"email profile\"\n" +
- "}";
+ return """
+ {
+ "access_token": "%s",
+ "expires_in": 300,
+ "refresh_expires_in": 1800,
+ "refresh_token": "%s",
+ "token_type": "bearer",
+ "not-before-policy": 0,
+ "session_state": "%s",
+ "scope": "email profile"
+ }
+ """.formatted(KEYCLOAK_ACCESS_TOKEN, KEYCLOAK_REFRESH_TOKEN, KEYCLOAK_SESSION_STATE);
}
@Override
public void stop() {
- if (wireMockServer != null) {
- wireMockServer.stop();
+ if (wireMockServer.get() != null) {
+ wireMockServer.get().stop();
+ wireMockServer.remove();
}
}
}
diff --git a/client/oidc/pom.xml b/client/oidc/pom.xml
index 0dfc506b4..c68eaf11c 100644
--- a/client/oidc/pom.xml
+++ b/client/oidc/pom.xml
@@ -27,6 +27,31 @@
quarkus-resteasy-client-oidc-filter
provided
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ org.jboss.resteasy
+ resteasy-core
+ test
+
\ No newline at end of file
diff --git a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java
index f37895c8a..677ee5b48 100644
--- a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java
+++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/ClassicOidcClientRequestFilterDelegate.java
@@ -7,6 +7,7 @@
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
+import jakarta.ws.rs.core.HttpHeaders;
import org.jboss.logging.Logger;
@@ -41,7 +42,7 @@ protected java.util.Optional clientId() {
public void filter(ClientRequestContext requestContext) throws IOException {
try {
String accessToken = this.getAccessToken();
- requestContext.getHeaders().add("Authorization", "Bearer " + accessToken);
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
} catch (DisabledOidcClientException ex) {
LOG.debug("Client is disabled, acquiring and propagating the token is not necessary");
} catch (RuntimeException ex) {
diff --git a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java
index c2dda5c80..42efbbf2a 100644
--- a/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java
+++ b/client/oidc/src/main/java/io/quarkiverse/openapi/generator/oidc/providers/OAuth2AuthenticationProvider.java
@@ -12,9 +12,10 @@
import org.slf4j.LoggerFactory;
import io.quarkiverse.openapi.generator.providers.AbstractAuthProvider;
+import io.quarkiverse.openapi.generator.providers.AuthUtils;
+import io.quarkiverse.openapi.generator.providers.ConfigCredentialsProvider;
import io.quarkiverse.openapi.generator.providers.CredentialsProvider;
import io.quarkiverse.openapi.generator.providers.OperationAuthInfo;
-import io.quarkus.oidc.common.runtime.OidcConstants;
public class OAuth2AuthenticationProvider extends AbstractAuthProvider {
@@ -30,14 +31,30 @@ public OAuth2AuthenticationProvider(String name,
validateConfig();
}
+ public OAuth2AuthenticationProvider(String name,
+ String openApiSpecId, OidcClientRequestFilterDelegate delegate, List operations) {
+ this(name, openApiSpecId, delegate, operations, new ConfigCredentialsProvider());
+ }
+
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
- if (isTokenPropagation()) {
- String bearerToken = getTokenForPropagation(requestContext.getHeaders());
- bearerToken = sanitizeBearerToken(bearerToken);
- requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, OidcConstants.BEARER_SCHEME + " " + bearerToken);
+ String bearerToken;
+
+ if (this.isTokenPropagation()) {
+ bearerToken = this.getTokenForPropagation(requestContext.getHeaders());
} else {
delegate.filter(requestContext);
+ bearerToken = this.getCredentialsProvider().getOauth2BearerToken(CredentialsProvider.CredentialsContext.builder()
+ .requestContext(requestContext)
+ .openApiSpecId(getOpenApiSpecId())
+ .authName(getName())
+ .build());
+ }
+
+ if (bearerToken != null && !bearerToken.isBlank()) {
+ requestContext.getHeaders().remove(HttpHeaders.AUTHORIZATION);
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION,
+ AuthUtils.authTokenOrBearer("Bearer", AbstractAuthProvider.sanitizeBearerToken(bearerToken)));
}
}
diff --git a/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/OAuth2AuthenticationProviderTest.java b/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/OAuth2AuthenticationProviderTest.java
new file mode 100644
index 000000000..8eaa1e2dc
--- /dev/null
+++ b/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/OAuth2AuthenticationProviderTest.java
@@ -0,0 +1,124 @@
+package io.quarkiverse.openapi.generator.oidc;
+
+import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderName;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.MultivaluedMap;
+
+import org.assertj.core.api.Assertions;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import io.quarkiverse.openapi.generator.AuthConfig;
+import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider;
+import io.quarkus.oidc.client.Tokens;
+
+@ExtendWith(MockitoExtension.class)
+public class OAuth2AuthenticationProviderTest {
+ private static final String OPEN_API_FILE_SPEC_ID = "open_api_file_spec_id_json";
+ private static final String AUTH_SCHEME_NAME = "auth_scheme_name";
+
+ private static final String ACCESS_TOKEN = "ACCESS_TOKEN";
+
+ private static final String PROPAGATED_TOKEN = "PROPAGATED_TOKEN";
+
+ private static final String HEADER_NAME = "HEADER_NAME";
+
+ @Mock
+ private ClientRequestContext requestContext;
+
+ private MultivaluedMap headers;
+
+ private ClassicOidcClientRequestFilterDelegate classicDelegate;
+
+ private static final Tokens token = new Tokens(ACCESS_TOKEN, Long.MAX_VALUE, null, "", Long.MAX_VALUE, null, "");
+
+ private OAuth2AuthenticationProvider provider;
+
+ @BeforeEach
+ void setUp() {
+ headers = new MultivaluedHashMap<>();
+ Mockito.lenient().doReturn(headers).when(requestContext).getHeaders();
+
+ classicDelegate = Mockito.mock(ClassicOidcClientRequestFilterDelegate.class);
+ Mockito.lenient().when(classicDelegate.awaitTokens()).thenReturn(token);
+ try {
+ Mockito.lenient().doCallRealMethod().when(classicDelegate).filter(requestContext);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ provider = createClassicProvider();
+
+ }
+
+ private OAuth2AuthenticationProvider createClassicProvider() {
+ return new OAuth2AuthenticationProvider(AUTH_SCHEME_NAME, OPEN_API_FILE_SPEC_ID, classicDelegate, List.of());
+ }
+
+ private void assertHeader(MultivaluedMap headers, String headerName, String value) {
+ Assertions.assertThat(headers.getFirst(headerName))
+ .isNotNull()
+ .isEqualTo(value);
+ }
+
+ static Stream filterWithPropagationTestValues() {
+ return Stream.of(
+ Arguments.of(null, "Bearer " + PROPAGATED_TOKEN),
+ Arguments.of(HEADER_NAME, "Bearer " + PROPAGATED_TOKEN));
+ }
+
+ @Test
+ void filterClassic() throws IOException {
+ filter(provider, "Bearer " + ACCESS_TOKEN);
+ }
+
+ private void filter(OAuth2AuthenticationProvider provider, String expectedAuthorizationHeader) throws IOException {
+ provider.filter(requestContext);
+ assertHeader(headers, HttpHeaders.AUTHORIZATION, expectedAuthorizationHeader);
+ }
+
+ @ParameterizedTest
+ @MethodSource("filterWithPropagationTestValues")
+ void filterWithPropagation(String headerName,
+ String expectedAuthorizationHeader) throws IOException {
+ String propagatedHeaderName;
+ if (headerName == null) {
+ propagatedHeaderName = propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME,
+ HttpHeaders.AUTHORIZATION);
+ } else {
+ propagatedHeaderName = propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME,
+ HEADER_NAME);
+ }
+ try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) {
+ Config mockedConfig = Mockito.mock(Config.class);
+ configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig);
+
+ when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION),
+ Boolean.class)).thenReturn(Optional.of(true));
+ when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME),
+ String.class)).thenReturn(Optional.of(headerName == null ? HttpHeaders.AUTHORIZATION : headerName));
+
+ headers.putSingle(propagatedHeaderName, PROPAGATED_TOKEN);
+ filter(provider, expectedAuthorizationHeader);
+ }
+ }
+
+}
diff --git a/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/ReactiveOAuth2AuthenticationProviderTest.java b/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/ReactiveOAuth2AuthenticationProviderTest.java
new file mode 100644
index 000000000..68169a3d8
--- /dev/null
+++ b/client/oidc/src/test/java/io/quarkiverse/openapi/generator/oidc/ReactiveOAuth2AuthenticationProviderTest.java
@@ -0,0 +1,136 @@
+package io.quarkiverse.openapi.generator.oidc;
+
+import static io.quarkiverse.openapi.generator.providers.AbstractAuthenticationPropagationHeadersFactory.propagationHeaderName;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import jakarta.ws.rs.core.MultivaluedMap;
+
+import org.assertj.core.api.Assertions;
+import org.eclipse.microprofile.config.Config;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.jboss.resteasy.reactive.client.impl.ClientRequestContextImpl;
+import org.jboss.resteasy.reactive.client.impl.RestClientRequestContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import io.quarkiverse.openapi.generator.AuthConfig;
+import io.quarkiverse.openapi.generator.oidc.providers.OAuth2AuthenticationProvider;
+import io.quarkus.oidc.client.Tokens;
+import io.smallrye.mutiny.Uni;
+
+@ExtendWith(MockitoExtension.class)
+public class ReactiveOAuth2AuthenticationProviderTest {
+ private static final String OPEN_API_FILE_SPEC_ID = "open_api_file_spec_id_json";
+ private static final String AUTH_SCHEME_NAME = "auth_scheme_name";
+
+ private static final String ACCESS_TOKEN = "REACTIVE_ACCESS_TOKEN";
+
+ private static final String PROPAGATED_TOKEN = "PROPAGATED_TOKEN";
+
+ private static final String HEADER_NAME = "HEADER_NAME";
+ @Mock
+ private ClientRequestContextImpl reactiveRequestContext;
+
+ @Mock
+ private RestClientRequestContext restClientRequestContext;
+ private MultivaluedMap headers;
+
+ private ReactiveOidcClientRequestFilterDelegate reactiveDelegate;
+ private static final Tokens token = new Tokens(ACCESS_TOKEN, Long.MAX_VALUE, null, "", Long.MAX_VALUE, null, "");
+ private static final Uni uniToken = Uni.createFrom().item(token);
+
+ private OAuth2AuthenticationProvider provider;
+
+ @BeforeEach
+ void setUp() {
+ headers = new MultivaluedHashMap<>();
+ Mockito.lenient().doReturn(headers).when(reactiveRequestContext).getHeaders();
+ Mockito.lenient().doReturn(restClientRequestContext).when(reactiveRequestContext).getRestClientRequestContext();
+ Mockito.lenient().doAnswer(invocationOnMock -> restClientRequestContext.setSuspended(true))
+ .when(restClientRequestContext).suspend();
+ Mockito.lenient().doAnswer(invocationOnMock -> restClientRequestContext.setSuspended(false))
+ .when(restClientRequestContext).resume();
+ reactiveDelegate = Mockito.mock(ReactiveOidcClientRequestFilterDelegate.class);
+ try {
+ Mockito.lenient().doCallRealMethod().when(reactiveDelegate).filter(Mockito.any(ClientRequestContext.class));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Mockito.lenient().doCallRealMethod().when(reactiveDelegate).filter(reactiveRequestContext);
+ Mockito.lenient().when(reactiveDelegate.getTokens()).thenReturn(uniToken);
+
+ provider = createReactiveProvider();
+ }
+
+ protected OAuth2AuthenticationProvider createReactiveProvider() {
+ return new OAuth2AuthenticationProvider(AUTH_SCHEME_NAME, OPEN_API_FILE_SPEC_ID, reactiveDelegate, List.of());
+ }
+
+ protected void assertHeader(MultivaluedMap headers, String headerName, String value) {
+ Assertions.assertThat(headers.getFirst(headerName))
+ .isNotNull()
+ .isEqualTo(value);
+ }
+
+ static Stream filterWithPropagationTestValues() {
+ return Stream.of(
+ Arguments.of(null, "Bearer " + PROPAGATED_TOKEN),
+ Arguments.of(HEADER_NAME, "Bearer " + PROPAGATED_TOKEN));
+ }
+
+ @Test
+ void filterReactive() throws IOException, InterruptedException {
+ filter(provider, "Bearer " + ACCESS_TOKEN);
+ }
+
+ private void filter(OAuth2AuthenticationProvider provider, String expectedAuthorizationHeader)
+ throws IOException, InterruptedException {
+ provider.filter(reactiveRequestContext);
+ while (reactiveRequestContext.getRestClientRequestContext().isSuspended()) {
+ Thread.sleep(1000);
+ }
+ assertHeader(headers, HttpHeaders.AUTHORIZATION, expectedAuthorizationHeader);
+ }
+
+ @ParameterizedTest
+ @MethodSource("filterWithPropagationTestValues")
+ void filterWithPropagation(String headerName,
+ String expectedAuthorizationHeader) throws IOException, InterruptedException {
+ String propagatedHeaderName;
+ if (headerName == null) {
+ propagatedHeaderName = propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME,
+ HttpHeaders.AUTHORIZATION);
+ } else {
+ propagatedHeaderName = propagationHeaderName(OPEN_API_FILE_SPEC_ID, AUTH_SCHEME_NAME,
+ HEADER_NAME);
+ }
+ try (MockedStatic configProviderMocked = Mockito.mockStatic(ConfigProvider.class)) {
+ Config mockedConfig = Mockito.mock(Config.class);
+ configProviderMocked.when(ConfigProvider::getConfig).thenReturn(mockedConfig);
+
+ when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION),
+ Boolean.class)).thenReturn(Optional.of(true));
+ when(mockedConfig.getOptionalValue(provider.getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME),
+ String.class)).thenReturn(Optional.of(headerName == null ? HttpHeaders.AUTHORIZATION : headerName));
+
+ headers.putSingle(propagatedHeaderName, PROPAGATED_TOKEN);
+ filter(provider, expectedAuthorizationHeader);
+ }
+ }
+}
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java
index d5a776fc1..d38a0d6fb 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/AbstractAuthProvider.java
@@ -60,22 +60,33 @@ public String getName() {
}
public boolean isTokenPropagation() {
- return ConfigProvider.getConfig()
- .getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION), Boolean.class)
- .orElse(false);
+ return isTokenPropagation(getOpenApiSpecId(), getName());
}
- public String getTokenForPropagation(MultivaluedMap httpHeaders) {
- String headerName = getHeaderName() != null ? getHeaderName() : HttpHeaders.AUTHORIZATION;
- String propagatedHeaderName = propagationHeaderName(getOpenApiSpecId(), getName(), headerName);
+ public static String getTokenForPropagation(MultivaluedMap httpHeaders, String openApiSpecId,
+ String authName) {
+ String headerName = getHeaderName(openApiSpecId, authName) != null ? getHeaderName(openApiSpecId, authName)
+ : HttpHeaders.AUTHORIZATION;
+ String propagatedHeaderName = propagationHeaderName(openApiSpecId, authName, headerName);
return Objects.toString(httpHeaders.getFirst(propagatedHeaderName));
}
+ public String getTokenForPropagation(MultivaluedMap httpHeaders) {
+ return getTokenForPropagation(httpHeaders, getOpenApiSpecId(), getName());
+ }
+
public String getHeaderName() {
return ConfigProvider.getConfig()
.getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME), String.class).orElse(null);
}
+ public static String getHeaderName(String openApiSpecId, String authName) {
+ return ConfigProvider.getConfig()
+ .getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.HEADER_NAME, openApiSpecId, authName),
+ String.class)
+ .orElse(null);
+ }
+
@Override
public List operationsToFilter() {
return applyToOperations;
@@ -88,4 +99,15 @@ public final String getCanonicalAuthConfigPropertyName(String authPropertyName)
public static String getCanonicalAuthConfigPropertyName(String authPropertyName, String openApiSpecId, String authName) {
return String.format(CANONICAL_AUTH_CONFIG_PROPERTY_NAME, openApiSpecId, authName, authPropertyName);
}
+
+ public static boolean isTokenPropagation(String openApiSpecId, String authName) {
+ return ConfigProvider.getConfig()
+ .getOptionalValue(getCanonicalAuthConfigPropertyName(AuthConfig.TOKEN_PROPAGATION, openApiSpecId, authName),
+ Boolean.class)
+ .orElse(false);
+ }
+
+ public CredentialsProvider getCredentialsProvider() {
+ return credentialsProvider;
+ }
}
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java
index 2f3db8393..f2467eb14 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ApiKeyAuthenticationProvider.java
@@ -59,7 +59,11 @@ && isUseAuthorizationHeaderValue()) {
}
private String getApiKey(ClientRequestContext requestContext) {
- return credentialsProvider.getApiKey(requestContext, getOpenApiSpecId(), getName());
+ return credentialsProvider.getApiKey(CredentialsProvider.CredentialsContext.builder()
+ .requestContext(requestContext)
+ .openApiSpecId(getOpenApiSpecId())
+ .authName(getName())
+ .build());
}
private boolean isUseAuthorizationHeaderValue() {
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java
index 10011a9b3..0ec39ed28 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BasicAuthenticationProvider.java
@@ -27,11 +27,19 @@ public BasicAuthenticationProvider(final String openApiSpecId, String name, List
}
private String getUsername(ClientRequestContext requestContext) {
- return credentialsProvider.getBasicUsername(requestContext, getOpenApiSpecId(), getName());
+ return credentialsProvider.getBasicUsername(CredentialsProvider.CredentialsContext.builder()
+ .requestContext(requestContext)
+ .openApiSpecId(getOpenApiSpecId())
+ .authName(getName())
+ .build());
}
private String getPassword(ClientRequestContext requestContext) {
- return credentialsProvider.getBasicPassword(requestContext, getOpenApiSpecId(), getName());
+ return credentialsProvider.getBasicPassword(CredentialsProvider.CredentialsContext.builder()
+ .requestContext(requestContext)
+ .openApiSpecId(getOpenApiSpecId())
+ .authName(getName())
+ .build());
}
@Override
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java
index 5454b1808..b51bf17b5 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/BearerAuthenticationProvider.java
@@ -40,6 +40,10 @@ public void filter(ClientRequestContext requestContext) throws IOException {
}
private String getBearerToken(ClientRequestContext requestContext) {
- return credentialsProvider.getBearerToken(requestContext, getOpenApiSpecId(), getName());
+ return credentialsProvider.getBearerToken(CredentialsProvider.CredentialsContext.builder()
+ .requestContext(requestContext)
+ .openApiSpecId(getOpenApiSpecId())
+ .authName(getName())
+ .build());
}
}
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java
index 8f78ee1e8..00ee1e142 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/ConfigCredentialsProvider.java
@@ -3,7 +3,7 @@
import jakarta.annotation.Priority;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Alternative;
-import jakarta.ws.rs.client.ClientRequestContext;
+import jakarta.ws.rs.core.HttpHeaders;
import org.eclipse.microprofile.config.ConfigProvider;
import org.slf4j.Logger;
@@ -26,41 +26,53 @@ public ConfigCredentialsProvider() {
}
@Override
- public String getApiKey(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ public String getApiKey(CredentialsContext input) {
final String key = ConfigProvider.getConfig()
- .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, openApiSpecId, authName),
+ .getOptionalValue(
+ AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, input.getOpenApiSpecId(),
+ input.getAuthName()),
String.class)
.orElse("");
if (key.isEmpty()) {
LOGGER.warn("configured {} property (see application.properties) is empty. hint: configure it.",
- AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, openApiSpecId, authName));
+ AbstractAuthProvider.getCanonicalAuthConfigPropertyName(API_KEY, input.getOpenApiSpecId(),
+ input.getAuthName()));
}
return key;
}
@Override
- public String getBasicUsername(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ public String getBasicUsername(CredentialsContext input) {
return ConfigProvider.getConfig()
- .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(USER_NAME, openApiSpecId, authName),
+ .getOptionalValue(
+ AbstractAuthProvider.getCanonicalAuthConfigPropertyName(USER_NAME, input.getOpenApiSpecId(),
+ input.getAuthName()),
String.class)
.orElse("");
}
@Override
- public String getBasicPassword(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ public String getBasicPassword(CredentialsContext input) {
return ConfigProvider.getConfig()
- .getOptionalValue(AbstractAuthProvider.getCanonicalAuthConfigPropertyName(PASSWORD, openApiSpecId, authName),
+ .getOptionalValue(
+ AbstractAuthProvider.getCanonicalAuthConfigPropertyName(PASSWORD, input.getOpenApiSpecId(),
+ input.getAuthName()),
String.class)
.orElse("");
}
@Override
- public String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ public String getBearerToken(CredentialsContext input) {
return ConfigProvider.getConfig()
.getOptionalValue(
- AbstractAuthProvider.getCanonicalAuthConfigPropertyName(BEARER_TOKEN, openApiSpecId, authName),
+ AbstractAuthProvider.getCanonicalAuthConfigPropertyName(BEARER_TOKEN, input.getOpenApiSpecId(),
+ input.getAuthName()),
String.class)
.orElse("");
}
+ @Override
+ public String getOauth2BearerToken(CredentialsContext input) {
+ return input.getRequestContext().getHeaderString(HttpHeaders.AUTHORIZATION);
+ }
}
diff --git a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java
index 3f15dd67c..02c4fa787 100644
--- a/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java
+++ b/client/runtime/src/main/java/io/quarkiverse/openapi/generator/providers/CredentialsProvider.java
@@ -12,36 +12,96 @@ public interface CredentialsProvider {
/**
* Gets the API Key given the OpenAPI definition and security schema
*
- * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension
- * @param authName The security schema for this API Key definition
+ * @param input the input data available to the method
* @return the API Key to use when filtering the request
*/
- String getApiKey(ClientRequestContext requestContext, String openApiSpecId, String authName);
+ String getApiKey(CredentialsContext input);
/**
* Gets the username given the OpenAPI definition and security schema
*
- * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension
- * @param authName The security schema for this Basic Authorization definition
+ * @param input the input data available to the method
* @return the username to use when filtering the request
*/
- String getBasicUsername(ClientRequestContext requestContext, String openApiSpecId, String authName);
+ String getBasicUsername(CredentialsContext input);
/**
* Gets the password given the OpenAPI definition and security schema
*
- * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension
- * @param authName The security schema for this Basic Authorization definition
+ * @param input the input data available to the method
* @return the password to use when filtering the request
*/
- String getBasicPassword(ClientRequestContext requestContext, String openApiSpecId, String authName);
+ String getBasicPassword(CredentialsContext input);
/**
* Gets the Bearer Token given the OpenAPI definition and security schema
*
- * @param openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension
- * @param authName The security schema for this Bearer Token definition
+ * @param input the input data available to the method
* @return the Bearer Token to use when filtering the request
*/
- String getBearerToken(ClientRequestContext requestContext, String openApiSpecId, String authName);
+ String getBearerToken(CredentialsContext input);
+
+ /**
+ * Gets the OAuth2 Bearer Token given the OpenAPI definition and security schema
+ *
+ * @param input the input data available to the method
+ * @return the Bearer Token to use when filtering the request
+ */
+ String getOauth2BearerToken(CredentialsContext input);
+
+ class CredentialsContext {
+ // requestContext The current request context in which set the authorization header token
+ private ClientRequestContext requestContext;
+ // openApiSpecId the OpenAPI Spec identification as defined by the OpenAPI Extension
+ private String openApiSpecId;
+ // authName The security schema for this Bearer Token definition
+ private String authName;
+
+ public CredentialsContext(ClientRequestContext requestContext, String openApiSpecId, String authName) {
+ this.requestContext = requestContext;
+ this.openApiSpecId = openApiSpecId;
+ this.authName = authName;
+ }
+
+ public ClientRequestContext getRequestContext() {
+ return requestContext;
+ }
+
+ public String getOpenApiSpecId() {
+ return openApiSpecId;
+ }
+
+ public String getAuthName() {
+ return authName;
+ }
+
+ public static CredentialsContextBuilder builder() {
+ return new CredentialsContextBuilder();
+ }
+
+ public static class CredentialsContextBuilder {
+ private ClientRequestContext requestContext;
+ private String openApiSpecId;
+ private String authName;
+
+ public CredentialsContextBuilder requestContext(ClientRequestContext requestContext) {
+ this.requestContext = requestContext;
+ return this;
+ }
+
+ public CredentialsContextBuilder openApiSpecId(String openApiSpecId) {
+ this.openApiSpecId = openApiSpecId;
+ return this;
+ }
+
+ public CredentialsContextBuilder authName(String authName) {
+ this.authName = authName;
+ return this;
+ }
+
+ public CredentialsContext build() {
+ return new CredentialsContext(requestContext, openApiSpecId, authName);
+ }
+ }
+ }
}
diff --git a/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc b/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc
index 6d2c5b5a2..b5984c931 100644
--- a/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc
+++ b/docs/modules/ROOT/pages/includes/custom-auth-provider.adoc
@@ -27,7 +27,7 @@ import io.quarkiverse.openapi.generator.providers.CredentialsProvider;
@RequestScoped
@Alternative
-@Priority(10) // A higher priority than the default provider.
+@Priority(200) // A higher priority than the default provider.
public class RuntimeCredentialsProvider implements CredentialsProvider {
@Override