imp
return cacheHolder.cache();
}
+ public K generateKey() {
+ assert cacheHolder.keyGenerator() != null;
+ return cacheHolder.keyGenerator().get();
+ }
+
/**
* Imports a session from an external source into the {@link Cache}.
*
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangesUtils.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangesUtils.java
index f82b7a00115..6a58b42c0ae 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangesUtils.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/InfinispanChangesUtils.java
@@ -20,8 +20,10 @@ package org.keycloak.models.sessions.infinispan.changes;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import org.infinispan.Cache;
+import org.infinispan.affinity.KeyAffinityServiceFactory;
import org.infinispan.commons.util.concurrent.AggregateCompletionStage;
import org.infinispan.commons.util.concurrent.CompletableFutures;
import org.infinispan.commons.util.concurrent.CompletionStages;
@@ -39,6 +41,9 @@ import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
*/
public class InfinispanChangesUtils {
+ // by default, keep 128 keys ready to use
+ private static final int DEFAULT_KEY_BUFFER = 128;
+
private InfinispanChangesUtils() {
}
@@ -46,15 +51,38 @@ public class InfinispanChangesUtils {
String cacheName,
SessionFunction lifespanFunction,
SessionFunction maxIdleFunction) {
+ return createWithCache(session, cacheName, lifespanFunction, maxIdleFunction, null);
+ }
+
+ public static CacheHolder createWithCache(KeycloakSession session,
+ String cacheName,
+ SessionFunction lifespanFunction,
+ SessionFunction maxIdleFunction,
+ Supplier keyGenerator) {
var connections = session.getProvider(InfinispanConnectionProvider.class);
var cache = connections.>getCache(cacheName);
var sequencer = new ActionSequencer(connections.getExecutor(cacheName + "Replace"), false, null);
- return new CacheHolder<>(cache, sequencer, lifespanFunction, maxIdleFunction);
+ if (!cache.getCacheConfiguration().clustering().cacheMode().isClustered() || keyGenerator == null) {
+ return new CacheHolder<>(cache, sequencer, lifespanFunction, maxIdleFunction, keyGenerator);
+ }
+ var local = cache.getAdvancedCache().getRpcManager().getAddress();
+ var affinity = KeyAffinityServiceFactory.newLocalKeyAffinityService(
+ cache,
+ keyGenerator::get,
+ connections.getExecutor(cacheName + "KeyGenerator"),
+ DEFAULT_KEY_BUFFER);
+ return new CacheHolder<>(cache, sequencer, lifespanFunction, maxIdleFunction, () -> affinity.getKeyForAddress(local));
}
public static CacheHolder createWithoutCache(SessionFunction lifespanFunction,
SessionFunction maxIdleFunction) {
- return new CacheHolder<>(null, null, lifespanFunction, maxIdleFunction);
+ return new CacheHolder<>(null, null, lifespanFunction, maxIdleFunction, null);
+ }
+
+ public static CacheHolder createWithoutCache(SessionFunction lifespanFunction,
+ SessionFunction maxIdleFunction,
+ Supplier keyGenerator) {
+ return new CacheHolder<>(null, null, lifespanFunction, maxIdleFunction, keyGenerator);
}
public static void runOperationInCluster(
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/PersistentSessionsChangelogBasedTransaction.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/PersistentSessionsChangelogBasedTransaction.java
index 1c32c3ed8bf..d5c57babee6 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/PersistentSessionsChangelogBasedTransaction.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/PersistentSessionsChangelogBasedTransaction.java
@@ -77,6 +77,11 @@ abstract public class PersistentSessionsChangelogBasedTransaction get(K key, boolean offline) {
SessionUpdatesList myUpdates = getUpdates(offline).get(key);
if (myUpdates == null) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteInfinispanAuthenticationSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteInfinispanAuthenticationSessionProvider.java
index d7f817778db..edd32742a85 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteInfinispanAuthenticationSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteInfinispanAuthenticationSessionProvider.java
@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Objects;
import org.keycloak.cluster.ClusterProvider;
+import org.keycloak.common.util.SecretGenerator;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@@ -29,7 +30,6 @@ import org.keycloak.models.cache.infinispan.events.AuthenticationSessionAuthNote
import org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.RootAuthenticationSessionEntity;
import org.keycloak.models.sessions.infinispan.remote.transaction.AuthenticationSessionChangeLogTransaction;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.sessions.AuthenticationSessionCompoundId;
import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.sessions.RootAuthenticationSessionModel;
@@ -53,7 +53,7 @@ public class RemoteInfinispanAuthenticationSessionProvider implements Authentica
@Override
public RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm) {
- return createRootAuthenticationSession(realm, KeycloakModelUtils.generateId());
+ return createRootAuthenticationSession(realm, SecretGenerator.SECURE_ID_GENERATOR.get());
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteUserSessionProvider.java
index d566e8866ba..67bce8b4b4d 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remote/RemoteUserSessionProvider.java
@@ -37,6 +37,7 @@ import org.infinispan.commons.util.concurrent.CompletionStages;
import org.jboss.logging.Logger;
import org.keycloak.cluster.ClusterProvider;
import org.keycloak.common.Profile;
+import org.keycloak.common.util.SecretGenerator;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@@ -111,7 +112,7 @@ public class RemoteUserSessionProvider implements UserSessionProvider {
@Override
public UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId, UserSessionModel.SessionPersistenceState persistenceState) {
if (id == null) {
- id = KeycloakModelUtils.generateId();
+ id = SecretGenerator.SECURE_ID_GENERATOR.get();
}
var entity = RemoteUserSessionEntity.create(id, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId);
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java
index f7669dc5592..d892075bceb 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java
@@ -29,11 +29,14 @@ import org.infinispan.affinity.KeyGenerator;
import org.jboss.logging.Logger;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.sessions.infinispan.changes.CacheHolder;
import org.keycloak.sessions.StickySessionEncoderProvider;
/**
* @author Marek Posolda
+ * @deprecated not supported and to be removed. Check {@link CacheHolder#keyGenerator()}
*/
+@Deprecated(since = "26.4", forRemoval = true)
public class InfinispanKeyGenerator {
private static final Logger log = Logger.getLogger(InfinispanKeyGenerator.class);
diff --git a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/CacheConfigurator.java b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/CacheConfigurator.java
index 0475cf14d67..38603ec2950 100644
--- a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/CacheConfigurator.java
+++ b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/CacheConfigurator.java
@@ -482,8 +482,14 @@ public final class CacheConfigurator {
switch (cacheName) {
// Distributed Caches
case CLIENT_SESSION_CACHE_NAME:
- case USER_SESSION_CACHE_NAME:
case OFFLINE_CLIENT_SESSION_CACHE_NAME:
+ // Groups keys by user session ID.
+ if (clustered) {
+ builder.clustering().hash().groups()
+ .enabled()
+ .addGrouper(ClientSessionKeyGrouper.INSTANCE);
+ }
+ case USER_SESSION_CACHE_NAME:
case OFFLINE_USER_SESSION_CACHE_NAME:
if (clustered) {
builder.clustering().cacheMode(CacheMode.DIST_SYNC).hash().numOwners(1);
diff --git a/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/ClientSessionKeyGrouper.java b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/ClientSessionKeyGrouper.java
new file mode 100644
index 00000000000..f36e613bd26
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/spi/infinispan/impl/embedded/ClientSessionKeyGrouper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2025 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.spi.infinispan.impl.embedded;
+
+import org.infinispan.distribution.group.Grouper;
+import org.keycloak.models.sessions.infinispan.entities.EmbeddedClientSessionKey;
+
+/**
+ * A {@link Grouper} implementation that uses the User Session ID to assign the Client Session to the cache segment. It
+ * groups all the Client Sessions belonging to the same User Session in the same node where the User Session lives.
+ */
+public enum ClientSessionKeyGrouper implements Grouper {
+
+ INSTANCE;
+
+ // The Infinispan parser expects a constructor or a static "getInstance" method; fixes ClusterConfigKeepAliveDistTest.
+ public static ClientSessionKeyGrouper getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public Object computeGroup(EmbeddedClientSessionKey key, Object group) {
+ return key.userSessionId();
+ }
+
+ @Override
+ public Class getKeyType() {
+ return EmbeddedClientSessionKey.class;
+ }
+}
diff --git a/test-framework/core/src/main/java/org/keycloak/testframework/events/EventMatchers.java b/test-framework/core/src/main/java/org/keycloak/testframework/events/EventMatchers.java
index 85ac6448b65..5804c8dfc6e 100644
--- a/test-framework/core/src/main/java/org/keycloak/testframework/events/EventMatchers.java
+++ b/test-framework/core/src/main/java/org/keycloak/testframework/events/EventMatchers.java
@@ -2,9 +2,11 @@ package org.keycloak.testframework.events;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
import java.util.UUID;
+import java.util.regex.Pattern;
public class EventMatchers {
@@ -12,6 +14,32 @@ public class EventMatchers {
return new UUIDMatcher();
}
+ public static Matcher isCodeId() {
+ // Make the tests pass with the old and the new encoding of code IDs
+ return Matchers.anyOf(isBase64WithAtLeast128Bits(), isUUID());
+ }
+
+ public static Matcher isSessionId() {
+ // Make the tests pass with the old and the new encoding of sessions
+ return Matchers.anyOf(isBase64WithAtLeast128Bits(), isUUID());
+ }
+
+ public static Matcher isBase64WithAtLeast128Bits() {
+ return new TypeSafeMatcher<>() {
+ private static final Pattern BASE64 = Pattern.compile("[-A-Za-z0-9+/_]*");
+
+ @Override
+ protected boolean matchesSafely(String item) {
+ return item.length() >= 24 && item.matches(BASE64.pattern());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("not an base64 ID with at least 128bits");
+ }
+ };
+ }
+
private EventMatchers() {
}
diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/ConsentsTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/ConsentsTest.java
index d4948ba1681..42579078e7b 100644
--- a/tests/base/src/test/java/org/keycloak/tests/admin/ConsentsTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/admin/ConsentsTest.java
@@ -17,6 +17,7 @@
package org.keycloak.tests.admin;
+import org.hamcrest.MatcherAssert;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -41,6 +42,7 @@ import org.keycloak.testframework.annotations.InjectEvents;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
+import org.keycloak.testframework.events.EventMatchers;
import org.keycloak.testframework.events.Events;
import org.keycloak.testframework.injection.LifeCycle;
import org.keycloak.testframework.oauth.OAuthClient;
@@ -302,8 +304,7 @@ public class ConsentsTest {
Assertions.assertEquals(EventType.LOGIN.toString(), loginEvent.getType());
loginEvent.getDetails().forEach((key, value) -> {
switch (key) {
- case Details.CODE_ID ->
- Assertions.assertTrue(isUUID(value));
+ case Details.CODE_ID -> MatcherAssert.assertThat(value, EventMatchers.isCodeId());
case Details.USERNAME -> Assertions.assertEquals(userFromUserRealm.getUsername(), value);
case Details.CONSENT -> Assertions.assertEquals(Details.CONSENT_VALUE_NO_CONSENT_REQUIRED, value);
case Details.REDIRECT_URI -> Assertions.assertEquals("http://127.0.0.1:8500/callback/oauth", value);
diff --git a/tests/base/src/test/java/org/keycloak/tests/admin/ImpersonationTest.java b/tests/base/src/test/java/org/keycloak/tests/admin/ImpersonationTest.java
index 932568dc11a..a9160cd6b74 100644
--- a/tests/base/src/test/java/org/keycloak/tests/admin/ImpersonationTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/admin/ImpersonationTest.java
@@ -305,7 +305,7 @@ public class ImpersonationTest {
EventRepresentation event = events.poll();
Assertions.assertEquals(event.getType(), EventType.IMPERSONATE.toString());
- MatcherAssert.assertThat(event.getSessionId(), EventMatchers.isUUID());
+ MatcherAssert.assertThat(event.getSessionId(), EventMatchers.isSessionId());
Assertions.assertEquals(event.getUserId(), managedUser.getId());
Assertions.assertTrue(event.getDetails().values().stream().anyMatch(f -> f.equals(admin)));
Assertions.assertTrue(event.getDetails().values().stream().anyMatch(f -> f.equals(adminRealm)));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
index e5e7e891f6b..4469ed70765 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -47,6 +47,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
import static org.hamcrest.Matchers.emptyOrNullString;
import static org.hamcrest.Matchers.is;
@@ -118,7 +119,7 @@ public class AssertEvents implements TestRule {
//.detail(Details.AUTH_TYPE, AuthorizationEndpoint.CODE_AUTH_TYPE)
.detail(Details.REDIRECT_URI, Matchers.equalTo(DEFAULT_REDIRECT_URI))
.detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED)
- .session(isUUID());
+ .session(isSessionId());
}
public ExpectedEvent expectClientLogin() {
@@ -127,7 +128,7 @@ public class AssertEvents implements TestRule {
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.detail(Details.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS)
.removeDetail(Details.CODE_ID)
- .session(isUUID());
+ .session(isSessionId());
}
public ExpectedEvent expectSocialLogin() {
@@ -136,14 +137,14 @@ public class AssertEvents implements TestRule {
.detail(Details.USERNAME, DEFAULT_USERNAME)
.detail(Details.AUTH_METHOD, "form")
.detail(Details.REDIRECT_URI, Matchers.equalTo(DEFAULT_REDIRECT_URI))
- .session(isUUID());
+ .session(isSessionId());
}
public ExpectedEvent expectCodeToToken(String codeId, String sessionId) {
return expect(EventType.CODE_TO_TOKEN)
.detail(Details.CODE_ID, codeId)
.detail(Details.TOKEN_ID, isAccessTokenId(AuthorizationCodeGrantTypeFactory.GRANT_SHORTCUT))
- .detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
@@ -153,7 +154,7 @@ public class AssertEvents implements TestRule {
return expect(EventType.OAUTH2_DEVICE_VERIFY_USER_CODE)
.user((String) null)
.client(clientId)
- .detail(Details.CODE_ID, isUUID());
+ .detail(Details.CODE_ID, isCodeId());
}
public ExpectedEvent expectDeviceLogin(String clientId, String codeId, String userId) {
@@ -171,7 +172,7 @@ public class AssertEvents implements TestRule {
.user(userId)
.detail(Details.CODE_ID, codeId)
.detail(Details.TOKEN_ID, isAccessTokenId(DeviceGrantTypeFactory.GRANT_SHORTCUT))
- .detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(codeId);
@@ -182,7 +183,7 @@ public class AssertEvents implements TestRule {
.detail(Details.TOKEN_ID, isAccessTokenId(RefreshTokenGrantTypeFactory.GRANT_SHORTCUT))
.detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
- .detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.UPDATED_REFRESH_TOKEN_ID, isTokenId())
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
}
@@ -242,10 +243,10 @@ public class AssertEvents implements TestRule {
return expect(EventType.AUTHREQID_TO_TOKEN)
.detail(Details.CODE_ID, codeId)
.detail(Details.TOKEN_ID, isAccessTokenId(CibaGrantTypeFactory.GRANT_SHORTCUT))
- .detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
- .session(isUUID());
+ .session(isSessionId());
}
public ExpectedEvent expectClientPolicyError(EventType eventType, String error, String reason, String clientPolicyError, String clientPolicyErrorDetail) {
@@ -466,7 +467,34 @@ public class AssertEvents implements TestRule {
}
public static Matcher isCodeId() {
- return isUUID();
+ // Make the tests pass with the old and the new encoding of code IDs
+ return Matchers.anyOf(isBase64WithAtLeast128Bits(), isUUID());
+ }
+
+ public static Matcher isSessionId() {
+ // Make the tests pass with the old and the new encoding of sessions
+ return Matchers.anyOf(isBase64WithAtLeast128Bits(), isUUID());
+ }
+
+ public static Matcher isTokenId() {
+ // Make the tests pass with the old and the new encoding of token IDs
+ return Matchers.anyOf(isBase64WithAtLeast128Bits(), isUUID());
+ }
+
+ public static Matcher isBase64WithAtLeast128Bits() {
+ return new TypeSafeMatcher<>() {
+ private static final Pattern BASE64 = Pattern.compile("[-A-Za-z0-9+/_]*");
+
+ @Override
+ protected boolean matchesSafely(String item) {
+ return item.length() >= 24 && item.matches(BASE64.pattern());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("not an base64 ID with at least 128bits");
+ }
+ };
}
public static Matcher isUUID() {
@@ -491,7 +519,7 @@ public class AssertEvents implements TestRule {
if (items.length != 2) return false;
// Grant type shortcut starts at character 4th char and is 2-chars long
if (items[0].substring(3, 5).equals(expectedGrantShortcut)) return false;
- return isUUID().matches(items[1]);
+ return isTokenId().matches(items[1]);
}
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/AppInitiatedActionTotpSetupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/AppInitiatedActionTotpSetupTest.java
index f31723cbceb..3c869730970 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/AppInitiatedActionTotpSetupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/AppInitiatedActionTotpSetupTest.java
@@ -587,7 +587,7 @@ public class AppInitiatedActionTotpSetupTest extends AbstractAppInitiatedActionT
tokenResponse = sendTokenRequestAndGetResponse(loginEvent);
oauth.logoutForm().idTokenHint(tokenResponse.getIdToken()).withRedirect().open();
- events.expectLogout(null).session(AssertEvents.isUUID()).assertEvent();
+ events.expectLogout(null).session(AssertEvents.isSessionId()).assertEvent();
// test lookAheadWindow
realmRep = adminClient.realm("test").toRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
index 09b6a1365bc..ee7b76a45a2 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionTotpSetupTest.java
@@ -674,7 +674,7 @@ public class RequiredActionTotpSetupTest extends AbstractTestRealmKeycloakTest {
tokenResponse = sendTokenRequestAndGetResponse(loginEvent);
oauth.logoutForm().idTokenHint(tokenResponse.getIdToken()).withRedirect().open();
- events.expectLogout(null).session(AssertEvents.isUUID()).assertEvent();
+ events.expectLogout(null).session(AssertEvents.isSessionId()).assertEvent();
// test lookAheadWindow
realmRep = adminClient.realm("test").toRepresentation();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java
index 7c2b159dc58..a97a18762f0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/CIBATest.java
@@ -2933,7 +2933,7 @@ public class CIBATest extends AbstractClientPoliciesTest {
else assertThat(tokenRes.getErrorDescription(), is(equalTo("Session not active")));
RefreshToken rt = oauth.parseRefreshToken(refreshToken);
- return events.expectLogout(sessionId).client(TEST_CLIENT_NAME).user(rt.getSubject()).session(AssertEvents.isUUID()).clearDetails().assertEvent();
+ return events.expectLogout(sessionId).client(TEST_CLIENT_NAME).user(rt.getSubject()).session(AssertEvents.isSessionId()).clearDetails().assertEvent();
}
private EventRepresentation doTokenRevokeByRefreshToken(String refreshToken, String sessionId, String userId, boolean isOfflineAccess) throws IOException {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index bae12561c1b..684b6aeab33 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -26,6 +26,7 @@ import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.Form;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang3.RandomStringUtils;
+import org.hamcrest.MatcherAssert;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert;
import org.junit.Rule;
@@ -47,7 +48,6 @@ import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.credential.PasswordCredentialModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.SessionTimeoutHelper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.ClientRepresentation;
@@ -1100,7 +1100,7 @@ public class LoginTest extends AbstractChangeImportedUserPasswordsTest {
String authSessionId = decodedAuthSessionId.substring(0, decodedAuthSessionId.indexOf("."));
String signature = decodedAuthSessionId.substring(decodedAuthSessionId.indexOf(".") + 1);
Assert.assertNotNull(authSessionId);
- Assert.assertTrue(KeycloakModelUtils.isValidUUID(authSessionId));
+ MatcherAssert.assertThat(authSessionId, AssertEvents.isSessionId());
Assert.assertNotNull(signature);
testingClient.server().run(session-> {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 0eff4bbd908..ddec9030a5b 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -296,7 +296,7 @@ public class AuthorizationCodeTest extends AbstractKeycloakTest {
events.expect(EventType.LOGIN)
.user(AssertEvents.isUUID())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.USERNAME, "test-user@localhost")
.detail(OIDCLoginProtocol.RESPONSE_MODE_PARAM, OIDCResponseMode.FORM_POST.name().toLowerCase())
.detail(OAuth2Constants.REDIRECT_URI, redirectUri)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java
index be4393368a9..4322d26f70a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenRevocationTest.java
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.keycloak.testsuite.AssertEvents.isUUID;
+import static org.keycloak.testsuite.AssertEvents.isTokenId;
import static org.keycloak.testsuite.AbstractAdminTest.loadJson;
import java.io.IOException;
@@ -318,7 +318,7 @@ public class TokenRevocationTest extends AbstractKeycloakTest {
events.expect(EventType.REVOKE_GRANT)
.session(tokenResponse.getSessionState())
- .detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, expectedTokenType)
.client("test-app")
.assertEvent(true);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/hok/HoKTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/hok/HoKTest.java
index 4509fea31f8..e1af63626b0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/hok/HoKTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/hok/HoKTest.java
@@ -103,7 +103,7 @@ public class HoKTest extends AbstractTestRealmKeycloakTest {
//.detail(Details.AUTH_TYPE, AuthorizationEndpoint.CODE_AUTH_TYPE)
.detail(Details.REDIRECT_URI, defaultRedirectUri)
.detail(Details.CONSENT, Details.CONSENT_VALUE_NO_CONSENT_REQUIRED)
- .session(isUUID());
+ .session(isSessionId());
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/tokenexchange/StandardTokenExchangeV2Test.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/tokenexchange/StandardTokenExchangeV2Test.java
index e45dba54a23..35c39f7f3cb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/tokenexchange/StandardTokenExchangeV2Test.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/tokenexchange/StandardTokenExchangeV2Test.java
@@ -227,7 +227,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.INVALID_REQUEST)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "requested_token_type unsupported")
.detail(Details.REQUESTED_TOKEN_TYPE, OAuth2Constants.REFRESH_TOKEN_TYPE)
.detail(Details.SUBJECT_TOKEN_CLIENT_ID, "subject-client")
@@ -252,7 +252,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
events.expect(EventType.TOKEN_EXCHANGE)
.client("requester-client")
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REQUESTED_TOKEN_TYPE, OAuth2Constants.ID_TOKEN_TYPE)
.detail(Details.SUBJECT_TOKEN_CLIENT_ID, "subject-client")
.assertEvent();
@@ -265,7 +265,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.INVALID_REQUEST)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "requested_token_type unsupported")
.detail(Details.REQUESTED_TOKEN_TYPE, OAuth2Constants.JWT_TOKEN_TYPE)
.detail(Details.SUBJECT_TOKEN_CLIENT_ID, "subject-client")
@@ -279,7 +279,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.INVALID_REQUEST)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "requested_token_type unsupported")
.detail(Details.REQUESTED_TOKEN_TYPE, OAuth2Constants.SAML2_TOKEN_TYPE)
.detail(Details.SUBJECT_TOKEN_CLIENT_ID, "subject-client")
@@ -293,7 +293,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.INVALID_REQUEST)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "requested_token_type unsupported")
.detail(Details.REQUESTED_TOKEN_TYPE, "WRONG_TOKEN_TYPE")
.detail(Details.SUBJECT_TOKEN_CLIENT_ID, "subject-client")
@@ -329,7 +329,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("invalid-requester-client")
.error(Errors.NOT_ALLOWED)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "client is not within the token audience")
.assertEvent();
}
@@ -742,7 +742,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.INVALID_REQUEST)
.user(john.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "Requested audience not available: target-client2")
.assertEvent();
@@ -788,9 +788,9 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
AccessToken exchangedToken = assertAudiencesAndScopes(response, List.of("target-client1"), List.of("default-scope1", "optional-scope2"));
events.expect(EventType.REFRESH_TOKEN)
.detail(Details.TOKEN_ID, exchangedToken.getId())
- .detail(Details.REFRESH_TOKEN_ID, AssertEvents.isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, AssertEvents.isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
- .detail(Details.UPDATED_REFRESH_TOKEN_ID, AssertEvents.isUUID())
+ .detail(Details.UPDATED_REFRESH_TOKEN_ID, AssertEvents.isTokenId())
.session(exchangedToken.getSessionId());
oauth.client("requester-client", "secret");
@@ -798,9 +798,9 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
exchangedToken = assertAudiencesAndScopes(response, List.of("target-client1"), List.of("default-scope1", "optional-scope2"));
events.expect(EventType.REFRESH_TOKEN)
.detail(Details.TOKEN_ID, exchangedToken.getId())
- .detail(Details.REFRESH_TOKEN_ID, AssertEvents.isUUID())
+ .detail(Details.REFRESH_TOKEN_ID, AssertEvents.isTokenId())
.detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
- .detail(Details.UPDATED_REFRESH_TOKEN_ID, AssertEvents.isUUID())
+ .detail(Details.UPDATED_REFRESH_TOKEN_ID, AssertEvents.isTokenId())
.session(exchangedToken.getSessionId());
}
}
@@ -844,7 +844,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.CONSENT_DENIED)
.user(mike.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "Missing consents for Token Exchange in client requester-client")
.assertEvent();
@@ -866,7 +866,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
.client("requester-client")
.error(Errors.CONSENT_DENIED)
.user(mike.getId())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.detail(Details.REASON, "Missing consents for Token Exchange in client requester-client")
.assertEvent();
@@ -1221,7 +1221,7 @@ public class StandardTokenExchangeV2Test extends AbstractClientPoliciesTest {
assertTrue(rep.isActive());
events.expect(EventType.INTROSPECT_TOKEN)
.user(AssertEvents.isUUID())
- .session(AssertEvents.isUUID())
+ .session(AssertEvents.isSessionId())
.client(clientId)
.assertEvent();
}
diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java
index 96544e7bffc..f969dbcef0f 100644
--- a/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java
+++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/session/UserSessionPersisterProviderTest.java
@@ -826,7 +826,7 @@ public class UserSessionPersisterProviderTest extends KeycloakModelTest {
int pageCount = 0;
boolean next = true;
List result = new ArrayList<>();
- String lastSessionId = "00000000-0000-0000-0000-000000000000";
+ String lastSessionId = "";
while (next) {
List sess = persister