keycloak-uncached

KEYCLOAK-5915 Support for sticky sessions managed by loadbalancer.

12/5/2017 7:47:08 PM

Changes

Details

diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
index 16ce4ab..bc7d1e8 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
@@ -58,14 +58,10 @@ public class AuthenticatedClientSessionEntity extends SessionEntity {
 
     private final UUID id;
 
-    private AuthenticatedClientSessionEntity(UUID id) {
+    public AuthenticatedClientSessionEntity(UUID id) {
         this.id = id;
     }
 
-    public AuthenticatedClientSessionEntity() {
-        this.id = UUID.randomUUID();
-    }
-
     public String getAuthMethod() {
         return authMethod;
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProvider.java
index 47174f8..ce2105a 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProvider.java
@@ -32,6 +32,7 @@ import org.keycloak.models.sessions.infinispan.entities.RootAuthenticationSessio
 import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
 import org.keycloak.models.sessions.infinispan.events.SessionEventsSenderTransaction;
 import org.keycloak.models.sessions.infinispan.stream.RootAuthenticationSessionPredicate;
+import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RealmInfoUtil;
 import org.keycloak.sessions.AuthenticationSessionProvider;
@@ -46,12 +47,14 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
 
     private final KeycloakSession session;
     private final Cache<String, RootAuthenticationSessionEntity> cache;
+    private final InfinispanKeyGenerator keyGenerator;
     protected final InfinispanKeycloakTransaction tx;
     protected final SessionEventsSenderTransaction clusterEventsSenderTx;
 
-    public InfinispanAuthenticationSessionProvider(KeycloakSession session, Cache<String, RootAuthenticationSessionEntity> cache) {
+    public InfinispanAuthenticationSessionProvider(KeycloakSession session, InfinispanKeyGenerator keyGenerator, Cache<String, RootAuthenticationSessionEntity> cache) {
         this.session = session;
         this.cache = cache;
+        this.keyGenerator = keyGenerator;
 
         this.tx = new InfinispanKeycloakTransaction();
         this.clusterEventsSenderTx = new SessionEventsSenderTransaction(session);
@@ -62,7 +65,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
 
     @Override
     public RootAuthenticationSessionModel createRootAuthenticationSession(RealmModel realm) {
-        String id = KeycloakModelUtils.generateId();
+        String id = keyGenerator.generateKeyString(session, cache);
         return createRootAuthenticationSession(id, realm);
     }
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProviderFactory.java
index 4055885..02a6f4d 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanAuthenticationSessionProviderFactory.java
@@ -30,6 +30,7 @@ import org.keycloak.models.sessions.infinispan.entities.RootAuthenticationSessio
 import org.keycloak.models.sessions.infinispan.events.AbstractAuthSessionClusterListener;
 import org.keycloak.models.sessions.infinispan.events.ClientRemovedSessionEvent;
 import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
+import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
 import org.keycloak.models.utils.PostMigrationEvent;
 import org.keycloak.provider.ProviderEvent;
 import org.keycloak.provider.ProviderEventListener;
@@ -47,6 +48,8 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
 
     private static final Logger log = Logger.getLogger(InfinispanAuthenticationSessionProviderFactory.class);
 
+    private InfinispanKeyGenerator keyGenerator;
+
     private volatile Cache<String, RootAuthenticationSessionEntity> authSessionsCache;
 
     public static final String PROVIDER_ID = "infinispan";
@@ -105,7 +108,7 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
     @Override
     public AuthenticationSessionProvider create(KeycloakSession session) {
         lazyInit(session);
-        return new InfinispanAuthenticationSessionProvider(session, authSessionsCache);
+        return new InfinispanAuthenticationSessionProvider(session, keyGenerator, authSessionsCache);
     }
 
     private void updateAuthNotes(ClusterEvent clEvent) {
@@ -149,6 +152,8 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
                     InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class);
                     authSessionsCache = connections.getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME);
 
+                    keyGenerator = new InfinispanKeyGenerator();
+
                     ClusterProvider cluster = session.getProvider(ClusterProvider.class);
                     cluster.registerListener(AUTHENTICATION_SESSION_EVENTS, this::updateAuthNotes);
 
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProvider.java
index 0aca09f..871525d 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProvider.java
@@ -31,14 +31,20 @@ public class InfinispanStickySessionEncoderProvider implements StickySessionEnco
 
     private final KeycloakSession session;
     private final String myNodeName;
+    private final boolean shouldAttachRoute;
 
-    public InfinispanStickySessionEncoderProvider(KeycloakSession session, String myNodeName) {
+    public InfinispanStickySessionEncoderProvider(KeycloakSession session, String myNodeName, boolean shouldAttachRoute) {
         this.session = session;
         this.myNodeName = myNodeName;
+        this.shouldAttachRoute = shouldAttachRoute;
     }
 
     @Override
     public String encodeSessionId(String sessionId) {
+        if (!shouldAttachRoute) {
+            return sessionId;
+        }
+
         String nodeName = getNodeName(sessionId);
         if (nodeName != null) {
             return sessionId + '.' + nodeName;
@@ -49,11 +55,17 @@ public class InfinispanStickySessionEncoderProvider implements StickySessionEnco
 
     @Override
     public String decodeSessionId(String encodedSessionId) {
+        // Try to decode regardless if shouldAttachRoute is true/false. It's possible that some loadbalancers may forward the route information attached by them to the backend keycloak server. We need to remove it then.
         int index = encodedSessionId.indexOf('.');
         return index == -1 ? encodedSessionId : encodedSessionId.substring(0, index);
     }
 
     @Override
+    public boolean shouldAttachRoute() {
+        return shouldAttachRoute;
+    }
+
+    @Override
     public void close() {
 
     }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProviderFactory.java
index 2477b69..001e295 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanStickySessionEncoderProviderFactory.java
@@ -17,6 +17,7 @@
 
 package org.keycloak.models.sessions.infinispan;
 
+import org.jboss.logging.Logger;
 import org.keycloak.Config;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
 import org.keycloak.models.KeycloakSession;
@@ -30,6 +31,10 @@ import org.keycloak.sessions.StickySessionEncoderProviderFactory;
  */
 public class InfinispanStickySessionEncoderProviderFactory implements StickySessionEncoderProviderFactory {
 
+    private static final Logger log = Logger.getLogger(InfinispanStickySessionEncoderProviderFactory.class);
+
+
+    private boolean shouldAttachRoute;
 
     @Override
     public StickySessionEncoderProvider create(KeycloakSession session) {
@@ -41,11 +46,19 @@ public class InfinispanStickySessionEncoderProviderFactory implements StickySess
             myNodeName = null;
         }
 
-        return new InfinispanStickySessionEncoderProvider(session, myNodeName);
+        return new InfinispanStickySessionEncoderProvider(session, myNodeName, shouldAttachRoute);
     }
 
     @Override
     public void init(Config.Scope config) {
+        this.shouldAttachRoute = config.getBoolean("shouldAttachRoute", true);
+        log.debugf("Should attach route to the sticky session cookie: %b", shouldAttachRoute);
+
+    }
+
+    // Used for testing
+    public void setShouldAttachRoute(boolean shouldAttachRoute) {
+        this.shouldAttachRoute = shouldAttachRoute;
     }
 
     @Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index eb49db4..7c7679f 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -54,10 +54,10 @@ import org.keycloak.models.sessions.infinispan.stream.SessionPredicate;
 import org.keycloak.models.sessions.infinispan.stream.UserLoginFailurePredicate;
 import org.keycloak.models.sessions.infinispan.stream.UserSessionPredicate;
 import org.keycloak.models.sessions.infinispan.util.FuturesHelper;
+import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
 import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
 import org.keycloak.models.utils.SessionTimeoutHelper;
 
-import java.io.Serializable;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -96,10 +96,13 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     protected final LastSessionRefreshStore lastSessionRefreshStore;
     protected final LastSessionRefreshStore offlineLastSessionRefreshStore;
 
+    protected final InfinispanKeyGenerator keyGenerator;
+
     public InfinispanUserSessionProvider(KeycloakSession session,
                                          RemoteCacheInvoker remoteCacheInvoker,
                                          LastSessionRefreshStore lastSessionRefreshStore,
                                          LastSessionRefreshStore offlineLastSessionRefreshStore,
+                                         InfinispanKeyGenerator keyGenerator,
                                          Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache,
                                          Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache,
                                          Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache,
@@ -124,6 +127,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
 
         this.lastSessionRefreshStore = lastSessionRefreshStore;
         this.offlineLastSessionRefreshStore = offlineLastSessionRefreshStore;
+        this.keyGenerator = keyGenerator;
 
         session.getTransactionManager().enlistAfterCompletion(clusterEventsSenderTx);
         session.getTransactionManager().enlistAfterCompletion(sessionTx);
@@ -159,10 +163,10 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
 
     @Override
     public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
-        AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
+        final UUID clientSessionId = keyGenerator.generateKeyUUID(session, clientSessionCache);
+        AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity(clientSessionId);
         entity.setRealmId(realm.getId());
         entity.setTimestamp(Time.currentTime());
-        final UUID clientSessionId = entity.getId();
 
         InfinispanChangelogBasedTransaction<String, UserSessionEntity> userSessionUpdateTx = getTransaction(false);
         InfinispanChangelogBasedTransaction<UUID, AuthenticatedClientSessionEntity> clientSessionUpdateTx = getClientSessionTransaction(false);
@@ -178,6 +182,12 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
     }
 
     @Override
+    public UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
+        final String userSessionId = keyGenerator.generateKeyString(session, sessionCache);
+        return createUserSession(userSessionId, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId);
+    }
+
+    @Override
     public UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId) {
         UserSessionEntity entity = new UserSessionEntity();
         entity.setId(id);
@@ -872,9 +882,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
                                                                   InfinispanChangelogBasedTransaction<String, UserSessionEntity> userSessionUpdateTx,
                                                                   InfinispanChangelogBasedTransaction<UUID, AuthenticatedClientSessionEntity> clientSessionUpdateTx,
                                                                   boolean offline) {
-        AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity();
+        final UUID clientSessionId = keyGenerator.generateKeyUUID(session, getClientSessionCache(offline));
+        AuthenticatedClientSessionEntity entity = new AuthenticatedClientSessionEntity(clientSessionId);
         entity.setRealmId(sessionToImportInto.getRealm().getId());
-        final UUID clientSessionId = entity.getId();
 
         entity.setAction(clientSession.getAction());
         entity.setAuthMethod(clientSession.getProtocol());
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
index 697c3f2..dc6e261 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
@@ -51,6 +51,7 @@ import org.keycloak.models.sessions.infinispan.initializer.InfinispanCacheInitia
 import org.keycloak.models.sessions.infinispan.initializer.OfflinePersistentUserSessionLoader;
 import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheSessionListener;
 import org.keycloak.models.sessions.infinispan.remotestore.RemoteCacheSessionsLoader;
+import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
 import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.PostMigrationEvent;
@@ -80,6 +81,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
     private RemoteCacheInvoker remoteCacheInvoker;
     private LastSessionRefreshStore lastSessionRefreshStore;
     private LastSessionRefreshStore offlineLastSessionRefreshStore;
+    private InfinispanKeyGenerator keyGenerator;
 
     @Override
     public InfinispanUserSessionProvider create(KeycloakSession session) {
@@ -90,7 +92,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
         Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);
         Cache<LoginFailureKey, SessionEntityWrapper<LoginFailureEntity>> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
 
-        return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore, 
+        return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore, keyGenerator,
           cache, offlineSessionsCache, clientSessionCache, offlineClientSessionsCache, loginFailures);
     }
 
@@ -109,6 +111,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
                 if (event instanceof PostMigrationEvent) {
                     KeycloakSession session = ((PostMigrationEvent) event).getSession();
 
+                    keyGenerator = new InfinispanKeyGenerator();
                     checkRemoteCaches(session);
                     loadPersistentSessions(factory, getMaxErrors(), getSessionsPerSegment());
                     registerClusterListeners(session);
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
new file mode 100644
index 0000000..a899363
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 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.models.sessions.infinispan.util;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+
+import org.infinispan.Cache;
+import org.infinispan.affinity.KeyAffinityService;
+import org.infinispan.affinity.KeyAffinityServiceFactory;
+import org.infinispan.affinity.KeyGenerator;
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.sessions.StickySessionEncoderProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class InfinispanKeyGenerator {
+
+    private static final Logger log = Logger.getLogger(InfinispanKeyGenerator.class);
+
+
+    private final Map<String, KeyAffinityService> keyAffinityServices = new ConcurrentHashMap<>();
+
+
+    public String generateKeyString(KeycloakSession session, Cache<String, ?> cache) {
+        return generateKey(session, cache, new StringKeyGenerator());
+    }
+
+
+    public UUID generateKeyUUID(KeycloakSession session, Cache<UUID, ?> cache) {
+        return generateKey(session, cache, new UUIDKeyGenerator());
+    }
+
+
+    private <K> K generateKey(KeycloakSession session, Cache<K, ?> cache, KeyGenerator<K> keyGenerator) {
+        String cacheName = cache.getName();
+
+        // "wantsLocalKey" is true if route is not attached to the sticky session cookie. Without attached route, We want the key, which will be "owned" by this node.
+        // This is needed due the fact that external loadbalancer will attach route corresponding to our node, which will be the owner of the particular key, hence we
+        // will be able to lookup key locally.
+        boolean wantsLocalKey = !session.getProvider(StickySessionEncoderProvider.class).shouldAttachRoute();
+
+        if (wantsLocalKey && cache.getCacheConfiguration().clustering().cacheMode().isClustered()) {
+            KeyAffinityService<K> keyAffinityService = keyAffinityServices.get(cacheName);
+            if (keyAffinityService == null) {
+                keyAffinityService = createKeyAffinityService(cache, keyGenerator);
+                keyAffinityServices.put(cacheName, keyAffinityService);
+
+                log.debugf("Registered key affinity service for cache '%s'", cacheName);
+            }
+
+            return keyAffinityService.getKeyForAddress(cache.getCacheManager().getAddress());
+        } else {
+            return keyGenerator.getKey();
+        }
+
+    }
+
+
+    private <K> KeyAffinityService<K> createKeyAffinityService(Cache<K, ?> cache, KeyGenerator<K> keyGenerator) {
+        // SingleThreadExecutor is recommended due it needs the single thread and leave it in the WAITING state
+        return KeyAffinityServiceFactory.newLocalKeyAffinityService(
+                cache,
+                keyGenerator,
+                Executors.newSingleThreadExecutor(),
+                16);
+    }
+
+
+    private static class UUIDKeyGenerator implements KeyGenerator<UUID> {
+
+        @Override
+        public UUID getKey() {
+            return UUID.randomUUID();
+        }
+    }
+
+
+    private static class StringKeyGenerator implements KeyGenerator<String> {
+
+        @Override
+        public String getKey() {
+            return KeycloakModelUtils.generateId();
+        }
+    }
+}
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
index 7e55783..7bd0854 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
@@ -174,7 +174,7 @@ public class ConcurrencyJDGRemoveSessionTest {
         session.setStarted(Time.currentTime());
         session.setLastSessionRefresh(Time.currentTime());
 
-        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity();
+        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity(UUID.randomUUID());
         clientSession.setAuthMethod("saml");
         clientSession.setAction("something");
         clientSession.setTimestamp(1234);
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
index 2e4428a..2a26a3c 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
@@ -93,7 +93,7 @@ public class ConcurrencyJDGSessionsCacheTest {
         session.setStarted(Time.currentTime());
         session.setLastSessionRefresh(Time.currentTime());
 
-        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity();
+        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity(UUID.randomUUID());
         clientSession.setAuthMethod("saml");
         clientSession.setAction("something");
         clientSession.setTimestamp(1234);
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
index a9bb784..af9139f 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
@@ -71,7 +71,7 @@ public class DistributedCacheConcurrentWritesTest {
         session.setStarted(Time.currentTime());
         session.setLastSessionRefresh(Time.currentTime());
 
-        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity();
+        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity(UUID.randomUUID());
         clientSession.setAuthMethod("saml");
         clientSession.setAction("something");
         clientSession.setTimestamp(1234);
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
index d6bc350..23f25b7 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
@@ -71,7 +71,7 @@ public class DistributedCacheWriteSkewTest {
         session.setStarted(Time.currentTime());
         session.setLastSessionRefresh(Time.currentTime());
 
-        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity();
+        AuthenticatedClientSessionEntity clientSession = new AuthenticatedClientSessionEntity(UUID.randomUUID());
         clientSession.setAuthMethod("saml");
         clientSession.setAction("something");
         clientSession.setTimestamp(1234);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index cd265f2..fc67d4e 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -32,6 +32,7 @@ public interface UserSessionProvider extends Provider {
     AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession);
     AuthenticatedClientSessionModel getClientSession(UserSessionModel userSession, ClientModel client, UUID clientSessionId, boolean offline);
 
+    UserSessionModel createUserSession(RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
     UserSessionModel createUserSession(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId);
     UserSessionModel getUserSession(RealmModel realm, String id);
     List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user);
diff --git a/server-spi-private/src/main/java/org/keycloak/sessions/StickySessionEncoderProvider.java b/server-spi-private/src/main/java/org/keycloak/sessions/StickySessionEncoderProvider.java
index 69dad56..752fd01 100644
--- a/server-spi-private/src/main/java/org/keycloak/sessions/StickySessionEncoderProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/sessions/StickySessionEncoderProvider.java
@@ -24,8 +24,24 @@ import org.keycloak.provider.Provider;
  */
 public interface StickySessionEncoderProvider extends Provider {
 
+
+    /**
+     * @param sessionId
+     * @return Encoded value to be used as the value of sticky session cookie (AUTH_SESSION_ID cookie)
+     */
     String encodeSessionId(String sessionId);
 
+
+    /**
+     * @param encodedSessionId value of the sticky session cookie
+     * @return decoded value, which represents the actual ID of the {@link AuthenticationSessionModel}
+     */
     String decodeSessionId(String encodedSessionId);
 
+
+    /**
+     * @return true if information about route should be attached to the sticky session cookie by Keycloak. Otherwise it may be attached by loadbalancer.
+     */
+    boolean shouldAttachRoute();
+
 }
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index e74bc11..d1acce0 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -234,13 +234,12 @@ public class PolicyEvaluationService {
 
                 if (clientId != null) {
                     ClientModel clientModel = realm.getClientById(clientId);
-                    String id = KeycloakModelUtils.generateId();
 
-                    AuthenticationSessionModel authSession = keycloakSession.authenticationSessions().createRootAuthenticationSession(id, realm)
+                    AuthenticationSessionModel authSession = keycloakSession.authenticationSessions().createRootAuthenticationSession(realm)
                             .createAuthenticationSession(clientModel);
                     authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
                     authSession.setAuthenticatedUser(userModel);
-                    userSession = keycloakSession.sessions().createUserSession(id, realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
+                    userSession = keycloakSession.sessions().createUserSession(authSession.getParentSession().getId(), realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
 
                     AuthenticationManager.setRolesAndMappersInSession(authSession);
                     clientSession = TokenManager.attachAuthenticationSession(keycloakSession, userSession, authSession);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 1826edf..649bccb 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -699,9 +699,8 @@ public class TokenEndpoint {
                 }
             }
 
-            String sessionId = KeycloakModelUtils.generateId();
             tokenUser = requestedUser;
-            tokenSession = session.sessions().createUserSession(sessionId, realm, requestedUser, requestedUser.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
+            tokenSession = session.sessions().createUserSession(realm, requestedUser, requestedUser.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
         }
 
         String requestedIssuer = formParams.getFirst(OAuth2Constants.REQUESTED_ISSUER);
@@ -850,8 +849,7 @@ public class TokenEndpoint {
 
         UserModel user = importUserFromExternalIdentity(context);
 
-        String sessionId = KeycloakModelUtils.generateId();
-        UserSessionModel userSession = session.sessions().createUserSession(sessionId, realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "external-exchange", false, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "external-exchange", false, null, null);
         externalIdp.exchangeExternalComplete(userSession, context, formParams);
 
         // this must exist so that we can obtain access token from user session if idp's store tokens is off
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
index dcfd651..eaf7285 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -281,8 +281,7 @@ public class UserResource {
         }
         EventBuilder event = new EventBuilder(realm, session, clientConnection);
 
-        String sessionId = KeycloakModelUtils.generateId();
-        UserSessionModel userSession = session.sessions().createUserSession(sessionId, realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
         AuthenticationManager.createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
         URI redirect = AccountFormService.accountServiceApplicationPage(uriInfo).build(realm.getName());
         Map<String, Object> result = new HashMap<>();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AbstractClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AbstractClusterTest.java
index d5c9ff2..d15fa54 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AbstractClusterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AbstractClusterTest.java
@@ -8,6 +8,7 @@ import org.keycloak.models.Constants;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.arquillian.ContainerInfo;
+import org.keycloak.testsuite.client.KeycloakTestingClient;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,11 +27,17 @@ import static org.keycloak.testsuite.util.WaitUtils.pause;
  */
 public abstract class AbstractClusterTest extends AbstractKeycloakTest {
 
+    // Keep the following constants in sync with arquillian
+    public static final String QUALIFIER_AUTH_SERVER_NODE_1 = "auth-server-${auth.server}-backend1";
+    public static final String QUALIFIER_AUTH_SERVER_NODE_2 = "auth-server-${auth.server}-backend2";
+
     @ArquillianResource
     protected ContainerController controller;
 
     protected Map<ContainerInfo, Keycloak> backendAdminClients = new HashMap<>();
 
+    protected Map<ContainerInfo, KeycloakTestingClient> backendTestingClients = new HashMap<>();
+
     private int currentFailNodeIndex = 0;
 
     public int getClusterSize() {
@@ -101,6 +108,9 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
         if (!backendAdminClients.containsKey(node)) {
             backendAdminClients.put(node, createAdminClientFor(node));
         }
+        if (!backendTestingClients.containsKey(node)) {
+            backendTestingClients.put(node, createTestingClientFor(node));
+        }
     }
 
     protected Keycloak createAdminClientFor(ContainerInfo node) {
@@ -109,9 +119,16 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
                 MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID);
     }
 
+    protected KeycloakTestingClient createTestingClientFor(ContainerInfo node) {
+        log.info("Initializing testing client for " + node.getContextRoot() + "/auth");
+        return KeycloakTestingClient.getInstance(node.getContextRoot() + "/auth");
+    }
+
     protected void killBackendNode(ContainerInfo node) {
         backendAdminClients.get(node).close();
         backendAdminClients.remove(node);
+        backendTestingClients.get(node).close();
+        backendTestingClients.remove(node);
         log.info("Killing backend node: " + node);
         controller.kill(node.getQualifier());
     }
@@ -126,6 +143,16 @@ public abstract class AbstractClusterTest extends AbstractKeycloakTest {
         return adminClient;
     }
 
+    protected KeycloakTestingClient getTestingClientFor(ContainerInfo node) {
+        KeycloakTestingClient testingClient = backendTestingClients.get(node);
+
+        if (testingClient == null && node.equals(suiteContext.getAuthServerInfo())) {
+            testingClient = this.testingClient;
+        }
+
+        return testingClient;
+    }
+
     @Before
     public void beforeClusterTest() {
         failback();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionClusterTest.java
new file mode 100644
index 0000000..684b13d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionClusterTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017 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.testsuite.cluster;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.hamcrest.Matchers;
+import org.infinispan.Cache;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.models.sessions.infinispan.InfinispanStickySessionEncoderProviderFactory;
+import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.sessions.StickySessionEncoderProvider;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.client.KeycloakTestingClient;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
+import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+
+import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AuthenticationSessionClusterTest extends AbstractClusterTest {
+
+    @Deployment(name = "node0")
+    @TargetsContainer(QUALIFIER_AUTH_SERVER_NODE_1)
+    public static WebArchive deployDC0() {
+        return RunOnServerDeployment.create(
+                AuthenticationSessionClusterTest.class,
+                AbstractClusterTest.class,
+                AbstractTestRealmKeycloakTest.class,
+                KeycloakTestingClient.class
+        );
+    }
+
+
+    @Page
+    protected LoginPage loginPage;
+
+    @Page
+    protected LoginPasswordUpdatePage updatePasswordPage;
+
+
+    @Page
+    protected LoginUpdateProfilePage updateProfilePage;
+
+    @Page
+    protected AppPage appPage;
+
+
+    @Before
+    public void setup() {
+        try {
+            adminClient.realm("test").remove();
+        } catch (Exception ignore) {
+        }
+
+        RealmRepresentation testRealm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
+        adminClient.realms().create(testRealm);
+    }
+
+    @After
+    public void after() {
+        adminClient.realm("test").remove();
+    }
+
+
+    @Test
+    public void testAuthSessionCookieWithAttachedRoute() throws Exception {
+        // TODO Maybe add compatibility between cluster and cross-dc tests regarding route name (jboss.node.name). Cross-dc tests use arquillian container qualifier when cluster tests just 'node1' .
+//        String node1Route = backendNode(0).getArquillianContainer().getName();
+//        String node2Route = backendNode(1).getArquillianContainer().getName();
+
+        String accountServiceNode1URL = RealmsResource.accountUrl(UriBuilder.fromUri(backendNode(0).getUriBuilder().build() + "/auth")).build("test").toString();
+        String accountServiceNode2URL = RealmsResource.accountUrl(UriBuilder.fromUri(backendNode(1).getUriBuilder().build() + "/auth")).build("test").toString();
+
+        Set<String> visitedRoutes = new HashSet<>();
+        for (int i = 0; i < 20; i++) {
+            driver.navigate().to(accountServiceNode1URL);
+            String authSessionCookie = AuthenticationSessionFailoverClusterTest.getAuthSessionCookieValue(driver);
+
+            Assert.assertThat(authSessionCookie.length(), Matchers.greaterThan(36));
+            String route = authSessionCookie.substring(37);
+            visitedRoutes.add(route);
+
+            // Drop all cookies before continue
+            driver.manage().deleteAllCookies();
+        }
+
+        Assert.assertThat(visitedRoutes, Matchers.containsInAnyOrder("node1", "node2"));
+    }
+
+
+    @Test
+    public void testAuthSessionCookieWithoutRoute() throws Exception {
+        String accountServiceNode1URL = RealmsResource.accountUrl(UriBuilder.fromUri(backendNode(0).getUriBuilder().build() + "/auth")).build("test").toString();
+        String accountServiceNode2URL = RealmsResource.accountUrl(UriBuilder.fromUri(backendNode(1).getUriBuilder().build() + "/auth")).build("test").toString();
+
+        // Disable route on backend server
+        getTestingClientFor(backendNode(0)).server().run(session -> {
+            InfinispanStickySessionEncoderProviderFactory factory = (InfinispanStickySessionEncoderProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(StickySessionEncoderProvider.class, "infinispan");
+            factory.setShouldAttachRoute(false);
+        });
+
+        // Test routes
+        for (int i = 0; i < 20; i++) {
+            driver.navigate().to(accountServiceNode1URL);
+            String authSessionCookie = AuthenticationSessionFailoverClusterTest.getAuthSessionCookieValue(driver);
+
+            Assert.assertEquals(36, authSessionCookie.length());
+
+            // Drop all cookies before continue
+            driver.manage().deleteAllCookies();
+
+            // Check that route owner is always node1
+            getTestingClientFor(backendNode(0)).server().run(session -> {
+                Cache authSessionCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME);
+                String keyOwner = InfinispanUtil.getKeyPrimaryOwnerAddress(authSessionCache, authSessionCookie);
+                Assert.assertEquals("node1", keyOwner);
+            });
+        }
+
+        // Revert route on backend server
+        getTestingClientFor(backendNode(0)).server().run(session -> {
+            InfinispanStickySessionEncoderProviderFactory factory = (InfinispanStickySessionEncoderProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(StickySessionEncoderProvider.class, "infinispan");
+            factory.setShouldAttachRoute(true);
+        });
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionFailoverClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionFailoverClusterTest.java
index c1811f9..817bdc6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionFailoverClusterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/AuthenticationSessionFailoverClusterTest.java
@@ -18,7 +18,6 @@
 package org.keycloak.testsuite.cluster;
 
 import java.io.IOException;
-import java.util.List;
 
 import javax.mail.MessagingException;
 
@@ -30,7 +29,6 @@ import org.keycloak.models.UserModel;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.services.managers.AuthenticationSessionManager;
-import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.admin.ApiUtil;
 import org.keycloak.testsuite.pages.AppPage;
@@ -39,9 +37,9 @@ import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
 import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
 import org.keycloak.testsuite.util.UserBuilder;
 import org.openqa.selenium.Cookie;
+import org.openqa.selenium.WebDriver;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
 
@@ -113,14 +111,14 @@ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverCl
     protected void failoverTest(boolean expectSuccessfulFailover) throws IOException, MessagingException {
         loginPage.open();
 
-        String cookieValue1 = getAuthSessionCookieValue();
+        String cookieValue1 = getAuthSessionCookieValue(driver);
 
         // Login and assert on "updatePassword" page
         loginPage.login("login-test", "password");
         updatePasswordPage.assertCurrent();
 
         // Route didn't change
-        Assert.assertEquals(cookieValue1, getAuthSessionCookieValue());
+        Assert.assertEquals(cookieValue1, getAuthSessionCookieValue(driver));
 
         log.info("Authentication session cookie: " + cookieValue1);
 
@@ -137,7 +135,7 @@ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverCl
             //Action was successful
             updateProfilePage.assertCurrent();
 
-            String cookieValue2 = getAuthSessionCookieValue();
+            String cookieValue2 = getAuthSessionCookieValue(driver);
 
             log.info("Authentication session cookie after failover: " + cookieValue2);
 
@@ -163,7 +161,7 @@ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverCl
         appPage.assertCurrent();
     }
 
-    private String getAuthSessionCookieValue() {
+    static String getAuthSessionCookieValue(WebDriver driver) {
         Cookie authSessionCookie = driver.manage().getCookieNamed(AuthenticationSessionManager.AUTH_SESSION_ID);
         Assert.assertNotNull(authSessionCookie);
         return authSessionCookie.getValue();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
index 9d99efb..93ade25 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
@@ -161,6 +161,10 @@
                 <property name="bindHttpPortOffset">1</property>
                 <property name="route">node1</property>
                 <property name="remoteMode">${undertow.remote}</property>
+                <property name="keycloakConfigPropertyOverrides">{
+                    "keycloak.connectionsInfinispan.nodeName": "node1"
+                    }
+                </property>
             </configuration>
         </container>
         <container qualifier="auth-server-undertow-backend2" mode="manual" >
@@ -172,6 +176,10 @@
                 <property name="bindHttpPortOffset">2</property>
                 <property name="route">node2</property>
                 <property name="remoteMode">${undertow.remote}</property>
+                <property name="keycloakConfigPropertyOverrides">{
+                    "keycloak.connectionsInfinispan.nodeName": "node2"
+                    }
+                </property>
             </configuration>
         </container>
 
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
index 9162726..c8ba747 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
@@ -141,7 +141,7 @@ public class UserSessionInitializerTest {
 
     private UserSessionModel[] createSessions() {
         UserSessionModel[] sessions = new UserSessionModel[3];
-        sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
 
         Set<String> roles = new HashSet<String>();
         roles.add("one");
@@ -154,10 +154,10 @@ public class UserSessionInitializerTest {
         createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
         createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         resetSession();
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
index efa2f7d..2148991 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
@@ -230,7 +230,7 @@ public class UserSessionPersisterProviderTest {
         fooRealm.addClient("foo-app");
         session.users().addUser(fooRealm, "user3");
 
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
         createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         resetSession();
@@ -264,7 +264,7 @@ public class UserSessionPersisterProviderTest {
         fooRealm.addClient("bar-app");
         session.users().addUser(fooRealm, "user3");
 
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
         createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
         createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
@@ -371,7 +371,7 @@ public class UserSessionPersisterProviderTest {
 
     private UserSessionModel[] createSessions() {
         UserSessionModel[] sessions = new UserSessionModel[3];
-        sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
 
         Set<String> roles = new HashSet<String>();
         roles.add("one");
@@ -384,10 +384,10 @@ public class UserSessionPersisterProviderTest {
         createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
         createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         return sessions;
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
index d5085b0..26b535a 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -188,7 +188,7 @@ public class UserSessionProviderOfflineTest {
         fooRealm.addClient("foo-app");
         session.users().addUser(fooRealm, "user3");
 
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
         AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         resetSession();
@@ -237,7 +237,7 @@ public class UserSessionProviderOfflineTest {
         fooRealm.addClient("bar-app");
         session.users().addUser(fooRealm, "user3");
 
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
         createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
         createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
@@ -298,7 +298,7 @@ public class UserSessionProviderOfflineTest {
         fooRealm.addClient("foo-app");
         session.users().addUser(fooRealm, "user3");
 
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
         AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         resetSession();
@@ -425,7 +425,7 @@ public class UserSessionProviderOfflineTest {
 
     private UserSessionModel[] createSessions() {
         UserSessionModel[] sessions = new UserSessionModel[3];
-        sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
 
         Set<String> roles = new HashSet<String>();
         roles.add("one");
@@ -438,10 +438,10 @@ public class UserSessionProviderOfflineTest {
         createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
         createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         return sessions;
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 3e2a4dc..2d25a07 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -312,13 +312,13 @@ public class UserSessionProviderTest {
             Set<String> expired = new HashSet<String>();
 
             Time.setOffset(-(realm.getSsoSessionMaxLifespan() + 1));
-            UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+            UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
             expired.add(userSession.getId());
             AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
             Assert.assertEquals(userSession, clientSession.getUserSession());
 
             Time.setOffset(0);
-            UserSessionModel s = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true, null, null);
+            UserSessionModel s = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.1", "form", true, null, null);
             //s.setLastSessionRefresh(Time.currentTime() - (realm.getSsoSessionIdleTimeout() + 1));
             s.setLastSessionRefresh(0);
             expired.add(s.getId());
@@ -326,7 +326,7 @@ public class UserSessionProviderTest {
             Set<String> valid = new HashSet<String>();
             Set<String> validClientSessions = new HashSet<String>();
 
-            userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+            userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
             valid.add(userSession.getId());
             validClientSessions.add(session.sessions().createClientSession(realm, client, userSession).getId());
 
@@ -382,7 +382,7 @@ public class UserSessionProviderTest {
         try {
             for (int i = 0; i < 25; i++) {
                 Time.setOffset(i);
-                UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
+                UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
                 AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), userSession);
                 clientSession.setRedirectUri("http://redirect");
                 clientSession.setRoles(new HashSet<String>());
@@ -406,7 +406,7 @@ public class UserSessionProviderTest {
     @Test
     public void testCreateAndGetInSameTransaction() {
         ClientModel client = realm.getClientByClientId("test-app");
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         AuthenticatedClientSessionModel clientSession = createClientSession(client, userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, userSession.getId());
@@ -420,7 +420,7 @@ public class UserSessionProviderTest {
 
     @Test
     public void testAuthenticatedClientSessions() {
-        UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
 
         ClientModel client1 = realm.getClientByClientId("test-app");
         ClientModel client2 = realm.getClientByClientId("third-party");
@@ -599,7 +599,7 @@ public class UserSessionProviderTest {
 
     private UserSessionModel[] createSessions() {
         UserSessionModel[] sessions = new UserSessionModel[3];
-        sessions[0] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
+        sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
 
         Set<String> roles = new HashSet<String>();
         roles.add("one");
@@ -612,10 +612,10 @@ public class UserSessionProviderTest {
         createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
         createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[1] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
+        sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
-        sessions[2] = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
+        sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
         createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
 
         resetSession();
diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
index 4790e9c..bd5cb1e 100644
--- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
+++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/AbstractSessionCacheCommand.java
@@ -338,7 +338,7 @@ public abstract class AbstractSessionCacheCommand extends AbstractCommand {
                 UserModel user = batchSession.users().getUserByUsername(username, realm);
 
                 for (int i=0 ; i<countInIteration ; i++) {
-                    UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, user, username, "127.0.0.1", "form", false, null, null);
+                    UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, "127.0.0.1", "form", false, null, null);
 
                     session.sessions().createClientSession(userSession.getRealm(), client, userSession);
                 }
diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/PersistSessionsCommand.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/PersistSessionsCommand.java
index c8b6771..1e4cb48 100644
--- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/PersistSessionsCommand.java
+++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/PersistSessionsCommand.java
@@ -80,7 +80,7 @@ public class PersistSessionsCommand extends AbstractCommand {
                 UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
 
                 for (int i = 0; i < countInThisBatch; i++) {
-                    UserSessionModel userSession = session.sessions().createUserSession(KeycloakModelUtils.generateId(), realm, john, "john-doh@localhost", "127.0.0.2", "form", true, null, null);
+                    UserSessionModel userSession = session.sessions().createUserSession(realm, john, "john-doh@localhost", "127.0.0.2", "form", true, null, null);
                     AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, testApp, userSession);
                     clientSession.setRedirectUri("http://redirect");
                     clientSession.setNote("foo", "bar-" + i);