diff --git a/extensions/oidc-token-propagation/deployment/src/test/java/io/quarkus/oidc/token/propagation/OidcTokenPropagationTest.java b/extensions/oidc-token-propagation/deployment/src/test/java/io/quarkus/oidc/token/propagation/OidcTokenPropagationTest.java
index 74af1487421298..2ad093b9bec6d9 100644
--- a/extensions/oidc-token-propagation/deployment/src/test/java/io/quarkus/oidc/token/propagation/OidcTokenPropagationTest.java
+++ b/extensions/oidc-token-propagation/deployment/src/test/java/io/quarkus/oidc/token/propagation/OidcTokenPropagationTest.java
@@ -2,19 +2,21 @@
import static org.hamcrest.Matchers.equalTo;
-import java.util.Set;
-
+import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import io.quarkus.test.QuarkusUnitTest;
import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.oidc.client.OidcTestClient;
import io.quarkus.test.oidc.server.OidcWiremockTestResource;
import io.restassured.RestAssured;
@QuarkusTestResource(OidcWiremockTestResource.class)
public class OidcTokenPropagationTest {
+ final static OidcTestClient client = new OidcTestClient();
+
private static Class>[] testClasses = {
FrontendResource.class,
ProtectedResource.class,
@@ -27,6 +29,11 @@ public class OidcTokenPropagationTest {
.addClasses(testClasses)
.addAsResource("application.properties"));
+ @AfterAll
+ public static void close() {
+ client.close();
+ }
+
@Test
public void testGetUserNameWithTokenPropagation() {
RestAssured.given().auth().oauth2(getBearerAccessToken())
@@ -37,7 +44,7 @@ public void testGetUserNameWithTokenPropagation() {
}
public String getBearerAccessToken() {
- return OidcWiremockTestResource.getAccessToken("alice", Set.of("admin"));
+ return client.getAccessToken("alice", "alice");
}
}
diff --git a/test-framework/oidc-server/pom.xml b/test-framework/oidc-server/pom.xml
index 84b923627046f4..850d44d016a43d 100644
--- a/test-framework/oidc-server/pom.xml
+++ b/test-framework/oidc-server/pom.xml
@@ -29,6 +29,14 @@
io.quarkus
quarkus-test-common
+
+ io.smallrye.reactive
+ smallrye-mutiny-vertx-web-client
+
+
+ org.awaitility
+ awaitility
+
diff --git a/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/client/OidcTestClient.java b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/client/OidcTestClient.java
new file mode 100644
index 00000000000000..d0964004442a34
--- /dev/null
+++ b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/client/OidcTestClient.java
@@ -0,0 +1,204 @@
+package io.quarkus.test.oidc.client;
+
+import static org.awaitility.Awaitility.await;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Map;
+
+import org.eclipse.microprofile.config.ConfigProvider;
+
+import io.vertx.core.MultiMap;
+import io.vertx.core.Vertx;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.ext.web.client.WebClient;
+
+public class OidcTestClient {
+
+ private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10);
+ private final static String CLIENT_AUTH_SERVER_URL_PROP = "client.quarkus.oidc.auth-server-url";
+ private final static String AUTH_SERVER_URL_PROP = "quarkus.oidc.auth-server-url";
+ private final static String CLIENT_ID_PROP = "quarkus.oidc.client-id";
+ private final static String CLIENT_SECRET_PROP = "quarkus.oidc.credentials.secret";
+
+ Vertx vertx = Vertx.vertx();
+ WebClient client = WebClient.create(vertx);
+
+ private String authServerUrl;
+ private String tokenUrl;
+
+ /**
+ * Get an access token a client_credentials grant.
+ * Client id must be configured with the `quarkus.oidc.client-id` property.
+ * Client secret must be configured with the `quarkus.oidc.credentials.secret` property.
+ */
+ public String getClientAccessToken() {
+ return getClientAccessToken(null);
+ }
+
+ /**
+ * Get an access token a client_credentials grant with additional properties.
+ * Client id must be configured with the `quarkus.oidc.client-id` property.
+ * Client secret must be configured with the `quarkus.oidc.credentials.secret` property.
+ */
+ public String getClientAccessToken(Map extraProps) {
+ return getClientAccessToken(getClientId(), getClientSecret(), extraProps);
+ }
+
+ /**
+ * Get an access token from the default tenant realm using a client_credentials grant with a
+ * the provided client id and secret.
+ */
+ public String getClientAccessToken(String clientId, String clientSecret) {
+ return getClientAccessToken(clientId, clientSecret, null);
+ }
+
+ /**
+ * Get an access token using a client_credentials grant with the provided client id and secret,
+ * and additional properties.
+ */
+ public String getClientAccessToken(String clientId, String clientSecret, Map extraProps) {
+ MultiMap requestMap = MultiMap.caseInsensitiveMultiMap();
+ requestMap.add("grant_type", "client_credentials")
+ .add("client_id", clientId);
+ if (clientSecret != null && !clientSecret.isBlank()) {
+ requestMap.add("client_secret", clientSecret);
+ }
+ return getAccessTokenInternal(requestMap, extraProps);
+ }
+
+ /**
+ * Get an access token from the default tenant realm using a password grant with the provided user name, user secret.
+ * Client id must be configured with the `quarkus.oidc.client-id` property.
+ * Client secret must be configured with the `quarkus.oidc.credentials.secret` property.
+ */
+ public String getAccessToken(String userName, String userSecret) {
+ return getAccessToken(userName, userSecret, null);
+ }
+
+ /**
+ * Get an access token from the default tenant realm using a password grant with the provided user name, user secret,
+ * and additional properties.
+ * Client id must be configured with the `quarkus.oidc.client-id` property.
+ * Client secret must be configured with the `quarkus.oidc.credentials.secret` property.
+ */
+ public String getAccessToken(String userName, String userSecret, Map extraProps) {
+ return getAccessToken(getClientId(), getClientSecret(), userName, userSecret, extraProps);
+ }
+
+ /**
+ * Get an access token from the default tenant realm using a password grant with the provided client id, client secret, user
+ * name, user secret, client
+ * id and user secret.
+ */
+ public String getAccessToken(String clientId, String clientSecret, String userName, String userSecret) {
+ return getAccessToken(userName, userSecret, clientId, clientSecret, null);
+ }
+
+ /**
+ * Get an access token using a password grant with the provided user name, user secret, client
+ * id and secret, and scopes.
+ */
+ public String getAccessToken(String clientId, String clientSecret, String userName, String userSecret,
+ Map extraProps) {
+
+ MultiMap requestMap = MultiMap.caseInsensitiveMultiMap();
+ requestMap.add("grant_type", "password")
+ .add("username", userName)
+ .add("password", userSecret);
+
+ requestMap.add("client_id", clientId);
+ if (clientSecret != null && !clientSecret.isBlank()) {
+ requestMap.add("client_secret", clientSecret);
+ }
+ return getAccessTokenInternal(requestMap, extraProps);
+ }
+
+ private String getAccessTokenInternal(MultiMap requestMap, Map extraProps) {
+
+ if (extraProps != null) {
+ requestMap = requestMap.addAll(extraProps);
+ }
+
+ var result = client.postAbs(getTokenUrl())
+ .putHeader("Content-Type", "application/x-www-form-urlencoded")
+ .sendBuffer(encodeForm(requestMap));
+ await().atMost(REQUEST_TIMEOUT).until(result::isComplete);
+
+ return result.result().bodyAsJsonObject().getString("access_token");
+ }
+
+ private String getClientId() {
+ return getPropertyValue(CLIENT_ID_PROP);
+ }
+
+ private String getClientSecret() {
+ return getPropertyValue(CLIENT_SECRET_PROP);
+ }
+
+ /**
+ * Return URL string configured with a 'quarkus.oidc.auth-server' property.
+ */
+ public String getAuthServerUrl() {
+ if (authServerUrl == null) {
+ authServerUrl = getOptionalPropertyValue(CLIENT_AUTH_SERVER_URL_PROP, AUTH_SERVER_URL_PROP);
+ }
+ return authServerUrl;
+ }
+
+ /**
+ * Return URL string configured with a 'quarkus.oidc.auth-server' property.
+ */
+ public String getTokenUrl() {
+ if (tokenUrl == null) {
+ getAuthServerUrl();
+ var result = client.getAbs(authServerUrl + "/.well-known/openid-configuration")
+ .send();
+ await().atMost(REQUEST_TIMEOUT).until(result::isComplete);
+ tokenUrl = result.result().bodyAsJsonObject().getString("token_endpoint");
+ }
+ return tokenUrl;
+ }
+
+ private String getPropertyValue(String prop) {
+ return ConfigProvider.getConfig().getValue(prop, String.class);
+ }
+
+ private String getOptionalPropertyValue(String prop, String defaultProp) {
+ return ConfigProvider.getConfig().getOptionalValue(prop, String.class)
+ .orElseGet(() -> ConfigProvider.getConfig().getValue(defaultProp, String.class));
+ }
+
+ public static Buffer encodeForm(MultiMap form) {
+ Buffer buffer = Buffer.buffer();
+ for (Map.Entry entry : form) {
+ if (buffer.length() != 0) {
+ buffer.appendByte((byte) '&');
+ }
+ buffer.appendString(entry.getKey());
+ buffer.appendByte((byte) '=');
+ buffer.appendString(urlEncode(entry.getValue()));
+ }
+ return buffer;
+ }
+
+ private static String urlEncode(String value) {
+ try {
+ return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public void close() {
+ if (client != null) {
+ client.close();
+ client = null;
+ }
+ if (vertx != null) {
+ vertx.close().toCompletionStage().toCompletableFuture().join();
+ vertx = null;
+ }
+ }
+}
diff --git a/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
index 7b4ffff14d1f6f..8c1533edc2430c 100644
--- a/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
+++ b/test-framework/oidc-server/src/main/java/io/quarkus/test/oidc/server/OidcWiremockTestResource.java
@@ -205,6 +205,9 @@ public Map start() {
"")
.withTransformers("response-template")));
+ definePasswordGrantTokenStub();
+ defineClientCredGrantTokenStub();
+
LOG.infof("Keycloak started in mock mode: %s", server.baseUrl());
Map conf = new HashMap<>();
conf.put("keycloak.url", server.baseUrl() + "/auth");
@@ -293,6 +296,28 @@ private void defineCodeFlowAuthorizationMockTokenStub() {
"}")));
}
+ private void definePasswordGrantTokenStub() {
+ server.stubFor(post("/auth/realms/quarkus/token")
+ .withRequestBody(containing("grant_type=password"))
+ .willReturn(aResponse()
+ .withHeader("Content-Type", "application/json")
+ .withBody("{\n" +
+ " \"access_token\": \""
+ + getAccessToken("alice", getAdminRoles()) + "\",\n" +
+ " \"refresh_token\": \"07e08903-1263-4dd1-9fd1-4a59b0db5283\"}")));
+ }
+
+ private void defineClientCredGrantTokenStub() {
+ server.stubFor(post("/auth/realms/quarkus/token")
+ .withRequestBody(containing("grant_type=client_credentials"))
+ .willReturn(aResponse()
+ .withHeader("Content-Type", "application/json")
+ .withBody("{\n" +
+ " \"access_token\": \""
+ + getAccessToken("alice", getAdminRoles()) + "\",\n" +
+ " \"refresh_token\": \"07e08903-1263-4dd1-9fd1-4a59b0db5283\"}")));
+ }
+
private void defineCodeFlowAuthorizationMockEncryptedTokenStub() {
server.stubFor(post("/auth/realms/quarkus/encrypted-id-token")
.withRequestBody(containing("authorization_code"))