diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 87a682ec29e3a..be2308d31b6b9 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -187,7 +187,7 @@
5.8.0
4.13.0
2.0.3.Final
- 23.0.7
+ 24.0.4
1.15.1
3.43.0
2.27.1
diff --git a/build-parent/pom.xml b/build-parent/pom.xml
index aaa551740eba3..8d4d5d295370e 100644
--- a/build-parent/pom.xml
+++ b/build-parent/pom.xml
@@ -107,7 +107,7 @@
- 23.0.7
+ 24.0.4
19.0.3
quay.io/keycloak/keycloak:${keycloak.version}
quay.io/keycloak/keycloak:${keycloak.wildfly.version}-legacy
diff --git a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc
index fd3d37c2468ab..32ebe11900fc0 100644
--- a/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc
+++ b/docs/src/main/asciidoc/security-openid-connect-dev-services.adoc
@@ -258,7 +258,7 @@ For more information, see xref:security-oidc-bearer-token-authentication.adoc#in
[[keycloak-initialization]]
=== Keycloak initialization
-The `quay.io/keycloak/keycloak:23.0.7` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default.
+The `quay.io/keycloak/keycloak:24.0.4` image which contains a Keycloak distribution powered by Quarkus is used to start a container by default.
`quarkus.keycloak.devservices.image-name` can be used to change the Keycloak image name.
For example, set it to `quay.io/keycloak/keycloak:19.0.3-legacy` to use a Keycloak distribution powered by WildFly.
Be aware that a Quarkus-based Keycloak distribution is only available starting from Keycloak `20.0.0`.
diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
index c18dd966b76fc..5a9ace88d43d5 100644
--- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
+++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/DevServicesConfig.java
@@ -34,7 +34,7 @@ public class DevServicesConfig {
* ends with `-legacy`.
* Override with `quarkus.keycloak.devservices.keycloak-x-image`.
*/
- @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:23.0.7")
+ @ConfigItem(defaultValue = "quay.io/keycloak/keycloak:24.0.4")
public String imageName;
/**
diff --git a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
index c2ef04ff3d074..f7f2e1cdff083 100644
--- a/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
+++ b/extensions/oidc/deployment/src/main/java/io/quarkus/oidc/deployment/devservices/keycloak/KeycloakDevServicesProcessor.java
@@ -109,7 +109,8 @@ public class KeycloakDevServicesProcessor {
private static final String KEYCLOAK_QUARKUS_HOSTNAME = "KC_HOSTNAME";
private static final String KEYCLOAK_QUARKUS_ADMIN_PROP = "KEYCLOAK_ADMIN";
private static final String KEYCLOAK_QUARKUS_ADMIN_PASSWORD_PROP = "KEYCLOAK_ADMIN_PASSWORD";
- private static final String KEYCLOAK_QUARKUS_START_CMD = "start --http-enabled=true --hostname-strict=false --hostname-strict-https=false";
+ private static final String KEYCLOAK_QUARKUS_START_CMD = "start --http-enabled=true --hostname-strict=false --hostname-strict-https=false "
+ + "--spi-user-profile-declarative-user-profile-config-file=/opt/keycloak/upconfig.json";
private static final String JAVA_OPTS = "JAVA_OPTS";
private static final String OIDC_USERS = "oidc.users";
@@ -509,6 +510,7 @@ protected void configure() {
addEnv(KEYCLOAK_QUARKUS_ADMIN_PASSWORD_PROP, KEYCLOAK_ADMIN_PASSWORD);
withCommand(startCommand.orElse(KEYCLOAK_QUARKUS_START_CMD)
+ (useSharedNetwork ? " --hostname-port=" + fixedExposedPort.getAsInt() : ""));
+ addUpConfigResource();
} else {
addEnv(KEYCLOAK_WILDFLY_USER_PROP, KEYCLOAK_ADMIN_USER);
addEnv(KEYCLOAK_WILDFLY_PASSWORD_PROP, KEYCLOAK_ADMIN_PASSWORD);
@@ -560,6 +562,13 @@ private void mapResource(String resourcePath, String mappedResource) {
}
}
+ private void addUpConfigResource() {
+ if (Thread.currentThread().getContextClassLoader().getResource("/dev-service/upconfig.json") != null) {
+ LOG.debug("Mapping the classpath /dev-service/upconfig.json resource to /opt/keycloak/upconfig.json");
+ withClasspathResourceMapping("/dev-service/upconfig.json", "/opt/keycloak/upconfig.json", BindMode.READ_ONLY);
+ }
+ }
+
private Integer findRandomPort() {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
diff --git a/extensions/oidc/deployment/src/main/resources/dev-service/upconfig.json b/extensions/oidc/deployment/src/main/resources/dev-service/upconfig.json
new file mode 100644
index 0000000000000..8487089bc90fd
--- /dev/null
+++ b/extensions/oidc/deployment/src/main/resources/dev-service/upconfig.json
@@ -0,0 +1,60 @@
+{
+ "attributes": [
+ {
+ "name": "username",
+ "displayName": "${username}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "min": 3, "max": 255 },
+ "username-prohibited-characters": {},
+ "up-username-not-idn-homograph": {}
+ }
+ },
+ {
+ "name": "email",
+ "displayName": "${email}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "email" : {},
+ "length": { "max": 255 }
+ }
+ },
+ {
+ "name": "firstName",
+ "displayName": "${firstName}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "max": 255 },
+ "person-name-prohibited-characters": {}
+ }
+ },
+ {
+ "name": "lastName",
+ "displayName": "${lastName}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "max": 255 },
+ "person-name-prohibited-characters": {}
+ }
+ }
+ ],
+ "groups": [
+ {
+ "name": "user-metadata",
+ "displayHeader": "User metadata",
+ "displayDescription": "Attributes, which refer to user metadata"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/integration-tests/keycloak-authorization/src/main/resources/application.properties b/integration-tests/keycloak-authorization/src/main/resources/application.properties
index 18e8a230fc3cf..9f980ee1c2310 100644
--- a/integration-tests/keycloak-authorization/src/main/resources/application.properties
+++ b/integration-tests/keycloak-authorization/src/main/resources/application.properties
@@ -92,3 +92,5 @@ admin-url=${keycloak.url}
# Configure Keycloak Admin Client
quarkus.keycloak.admin-client.server-url=${admin-url}
+
+quarkus.log.category."com.gargoylesoftware.htmlunit".level=ERROR
diff --git a/integration-tests/oidc-code-flow/src/main/resources/application.properties b/integration-tests/oidc-code-flow/src/main/resources/application.properties
index 0d61acc332ab5..5aef28a6fe64c 100644
--- a/integration-tests/oidc-code-flow/src/main/resources/application.properties
+++ b/integration-tests/oidc-code-flow/src/main/resources/application.properties
@@ -1,4 +1,5 @@
quarkus.keycloak.devservices.create-realm=false
+quarkus.keycloak.devservices.show-logs=true
# Default tenant configurationf
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
diff --git a/integration-tests/oidc-tenancy/src/main/resources/application.properties b/integration-tests/oidc-tenancy/src/main/resources/application.properties
index 88bf88c41c0e1..1b5a5f2efb9a1 100644
--- a/integration-tests/oidc-tenancy/src/main/resources/application.properties
+++ b/integration-tests/oidc-tenancy/src/main/resources/application.properties
@@ -135,7 +135,7 @@ quarkus.http.auth.permission.authenticated.policy=authenticated
smallrye.jwt.sign.key.location=/privateKey.pem
smallrye.jwt.new-token.lifespan=5
-quarkus.log.category."com.gargoylesoftware.htmlunit.javascript.host.css.CSSStyleSheet".level=FATAL
+quarkus.log.category."com.gargoylesoftware.htmlunit".level=ERROR
quarkus.http.auth.proactive=false
quarkus.native.additional-build-args=-H:IncludeResources=.*\\.pem
diff --git a/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java b/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
index 38a85d30756fb..f538817bdd679 100644
--- a/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
+++ b/integration-tests/oidc-tenancy/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
@@ -11,13 +11,17 @@
import java.io.IOException;
import java.net.URI;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import com.gargoylesoftware.htmlunit.CookieManager;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
import com.gargoylesoftware.htmlunit.WebClient;
@@ -307,7 +311,9 @@ public void testReAuthenticateWhenSwitchingTenants() throws IOException {
page = loginForm.getInputByName("login").click();
assertEquals("tenant-web-app2:alice", page.getBody().asNormalizedText());
assertNull(getSessionCookie(webClient, "tenant-web-app"));
- assertNotNull(getSessionCookie(webClient, "tenant-web-app2"));
+ List sessionCookieChunks = getSessionCookies(webClient, "tenant-web-app2");
+ assertNotNull(sessionCookieChunks);
+ assertEquals(2, sessionCookieChunks.size());
webClient.getCookieManager().clearCookies();
}
}
@@ -932,4 +938,17 @@ private Cookie getSessionAtCookie(WebClient webClient, String tenantId) {
private Cookie getSessionRtCookie(WebClient webClient, String tenantId) {
return webClient.getCookieManager().getCookie("q_session_rt" + (tenantId == null ? "_Default_test" : "_" + tenantId));
}
+
+ private List getSessionCookies(WebClient webClient, String tenantId) {
+ String sessionCookieNameChunk = "q_session" + (tenantId == null ? "" : "_" + tenantId) + "_chunk_";
+ CookieManager cookieManager = webClient.getCookieManager();
+ SortedMap sessionCookies = new TreeMap<>();
+ for (Cookie cookie : cookieManager.getCookies()) {
+ if (cookie.getName().startsWith(sessionCookieNameChunk)) {
+ sessionCookies.put(cookie.getName(), cookie);
+ }
+ }
+
+ return sessionCookies.isEmpty() ? null : new ArrayList(sessionCookies.values());
+ }
}
diff --git a/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java b/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java
index 29a9327e6d87b..bcd717025d989 100644
--- a/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java
+++ b/integration-tests/oidc-token-propagation/src/test/java/io/quarkus/it/keycloak/OidcTokenPropagationTest.java
@@ -53,7 +53,7 @@ public void testGetUserNameWithAccessTokenPropagation() {
//.statusCode(200)
//.body(equalTo("alice"));
.statusCode(500)
- .body(containsString("Feature not enabled"));
+ .body(containsString("Client not allowed to exchange"));
}
@Test
diff --git a/integration-tests/oidc/src/main/resources/upconfig.json b/integration-tests/oidc/src/main/resources/upconfig.json
new file mode 100644
index 0000000000000..8487089bc90fd
--- /dev/null
+++ b/integration-tests/oidc/src/main/resources/upconfig.json
@@ -0,0 +1,60 @@
+{
+ "attributes": [
+ {
+ "name": "username",
+ "displayName": "${username}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "min": 3, "max": 255 },
+ "username-prohibited-characters": {},
+ "up-username-not-idn-homograph": {}
+ }
+ },
+ {
+ "name": "email",
+ "displayName": "${email}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "email" : {},
+ "length": { "max": 255 }
+ }
+ },
+ {
+ "name": "firstName",
+ "displayName": "${firstName}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "max": 255 },
+ "person-name-prohibited-characters": {}
+ }
+ },
+ {
+ "name": "lastName",
+ "displayName": "${lastName}",
+ "permissions": {
+ "view": ["admin", "user"],
+ "edit": ["admin", "user"]
+ },
+ "validations": {
+ "length": { "max": 255 },
+ "person-name-prohibited-characters": {}
+ }
+ }
+ ],
+ "groups": [
+ {
+ "name": "user-metadata",
+ "displayHeader": "User metadata",
+ "displayDescription": "Attributes, which refer to user metadata"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/KeycloakXTestResourceLifecycleManager.java b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/KeycloakXTestResourceLifecycleManager.java
index 21c76533a6334..abe4321c0789d 100644
--- a/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/KeycloakXTestResourceLifecycleManager.java
+++ b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/KeycloakXTestResourceLifecycleManager.java
@@ -51,10 +51,12 @@ public Map start() {
keycloak = keycloak
.withClasspathResourceMapping(SERVER_KEYSTORE, SERVER_KEYSTORE_MOUNTED_PATH, BindMode.READ_ONLY)
.withClasspathResourceMapping(SERVER_TRUSTSTORE, SERVER_TRUSTSTORE_MOUNTED_PATH, BindMode.READ_ONLY)
+ .withClasspathResourceMapping("/upconfig.json", "/opt/keycloak/upconfig.json", BindMode.READ_ONLY)
.withCommand("build --https-client-auth=required")
.withCommand(String.format(
"start --https-client-auth=required --hostname-strict=false --hostname-strict-https=false"
- + " --https-key-store-file=%s --https-trust-store-file=%s --https-trust-store-password=password",
+ + " --https-key-store-file=%s --https-trust-store-file=%s --https-trust-store-password=password"
+ + " --spi-user-profile-declarative-user-profile-config-file=/opt/keycloak/upconfig.json",
SERVER_KEYSTORE_MOUNTED_PATH, SERVER_TRUSTSTORE_MOUNTED_PATH));
keycloak.start();
LOGGER.info(keycloak.getLogs());