keycloak-aplcache

Details

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 12019c1..dd0ce06 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
@@ -90,7 +90,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
     }
 
 
-    private RootAuthenticationSessionEntity getRootAuthenticationSessionEntity(RealmModel realm, String authSessionId) {
+    private RootAuthenticationSessionEntity getRootAuthenticationSessionEntity(String authSessionId) {
         // Chance created in this transaction
         RootAuthenticationSessionEntity entity = tx.get(cache, authSessionId);
 
@@ -181,7 +181,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
 
     @Override
     public RootAuthenticationSessionModel getRootAuthenticationSession(RealmModel realm, String authenticationSessionId) {
-        RootAuthenticationSessionEntity entity = getRootAuthenticationSessionEntity(realm, authenticationSessionId);
+        RootAuthenticationSessionEntity entity = getRootAuthenticationSessionEntity(authenticationSessionId);
         return wrap(realm, entity);
     }
 
diff --git a/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionProvider.java b/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionProvider.java
index d879b6e..bc32430 100644
--- a/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionProvider.java
@@ -20,6 +20,7 @@ package org.keycloak.sessions;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.provider.Provider;
+
 import java.util.Map;
 
 /**
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index 619a862..da2e376 100755
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -35,12 +35,9 @@ import org.keycloak.protocol.LoginProtocol.Error;
 import org.keycloak.services.ErrorPageException;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.AuthenticationSessionManager;
-import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.UserSessionCrossDCManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.LoginActionsService;
-import org.keycloak.services.util.CacheControlUtil;
-import org.keycloak.services.util.AuthenticationFlowURLHelper;
 import org.keycloak.sessions.AuthenticationSessionModel;
 import org.keycloak.sessions.RootAuthenticationSessionModel;
 
@@ -170,26 +167,25 @@ public abstract class AuthorizationEndpointBase {
 
     protected AuthenticationSessionModel createAuthenticationSession(ClientModel client, String requestState) {
         AuthenticationSessionManager manager = new AuthenticationSessionManager(session);
-        String authSessionId = manager.getCurrentAuthenticationSessionId(realm);
-        RootAuthenticationSessionModel rootAuthSession = authSessionId==null ? null : session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
+        RootAuthenticationSessionModel rootAuthSession = manager.getCurrentRootAuthenticationSession(realm);
+
         AuthenticationSessionModel authSession;
 
         if (rootAuthSession != null) {
-
             authSession = rootAuthSession.createAuthenticationSession(client);
 
             logger.debugf("Sent request to authz endpoint. Root authentication session with ID '%s' exists. Client is '%s' . Created new authentication session with tab ID: %s",
                     rootAuthSession.getId(), client.getClientId(), authSession.getTabId());
-
         } else {
-
-            UserSessionModel userSession = authSessionId == null ? null : new UserSessionCrossDCManager(session).getUserSessionIfExistsRemotely(realm, authSessionId);
+            UserSessionCrossDCManager userSessionCrossDCManager = new UserSessionCrossDCManager(session);
+            UserSessionModel userSession = userSessionCrossDCManager.getUserSessionIfExistsRemotely(manager, realm);
 
             if (userSession != null) {
-                rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(authSessionId, realm);
+                String userSessionId = userSession.getId();
+                rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(userSessionId, realm);
                 authSession = rootAuthSession.createAuthenticationSession(client);
                 logger.debugf("Sent request to authz endpoint. We don't have root authentication session with ID '%s' but we have userSession." +
-                        "Re-created root authentication session with same ID. Client is: %s . New authentication session tab ID: %s", authSessionId, client.getClientId(), authSession.getTabId());
+                        "Re-created root authentication session with same ID. Client is: %s . New authentication session tab ID: %s", userSessionId, client.getClientId(), authSession.getTabId());
             } else {
                 rootAuthSession = manager.createAuthenticationSession(realm, true);
                 authSession = rootAuthSession.createAuthenticationSession(client);
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 87df917..6a1e89b 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -67,6 +67,7 @@ import java.net.URI;
 import java.security.PublicKey;
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.AbstractMap.SimpleEntry;
 
 /**
  * Stateless object that manages authentication
@@ -223,20 +224,22 @@ public class AuthenticationManager {
         // Account management client is used as a placeholder
         ClientModel client = SystemClientUtil.getSystemClient(realm);
 
-        // Try to lookup current authSessionId from browser cookie. If doesn't exists, use the same as current userSession
-        String authSessionId = null;
+        String authSessionId;
+        RootAuthenticationSessionModel rootLogoutSession = null;
         boolean browserCookiePresent = false;
+
+        // Try to lookup current authSessionId from browser cookie. If doesn't exists, use the same as current userSession
         if (browserCookie) {
-            authSessionId = asm.getCurrentAuthenticationSessionId(realm);
+            rootLogoutSession = asm.getCurrentRootAuthenticationSession(realm);
         }
-        if (authSessionId != null) {
+        if (rootLogoutSession != null) {
+            authSessionId = rootLogoutSession.getId();
             browserCookiePresent = true;
         } else {
             authSessionId = userSession.getId();
+            rootLogoutSession = session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
         }
 
-        // Try to join existing logout session if it exists
-        RootAuthenticationSessionModel rootLogoutSession = session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
         if (rootLogoutSession == null) {
             rootLogoutSession = session.authenticationSessions().createRootAuthenticationSession(authSessionId, realm);
         }
@@ -615,7 +618,20 @@ public class AuthenticationManager {
         String path = getIdentityCookiePath(realm, uriInfo);
         expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, path, true, connection);
         expireCookie(realm, KEYCLOAK_SESSION_COOKIE, path, false, connection);
+
+        String oldPath = getOldCookiePath(realm, uriInfo);
+        expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, oldPath, true, connection);
+        expireCookie(realm, KEYCLOAK_SESSION_COOKIE, oldPath, false, connection);
     }
+    public static void expireOldIdentityCookie(RealmModel realm, UriInfo uriInfo, ClientConnection connection) {
+        logger.debug("Expiring old identity cookie with wrong path");
+
+        String oldPath = getOldCookiePath(realm, uriInfo);
+        expireCookie(realm, KEYCLOAK_IDENTITY_COOKIE, oldPath, true, connection);
+        expireCookie(realm, KEYCLOAK_SESSION_COOKIE, oldPath, false, connection);
+    }
+
+
     public static void expireRememberMeCookie(RealmModel realm, UriInfo uriInfo, ClientConnection connection) {
         logger.debug("Expiring remember me cookie");
         String path = getIdentityCookiePath(realm, uriInfo);
@@ -623,12 +639,25 @@ public class AuthenticationManager {
         expireCookie(realm, cookieName, path, true, connection);
     }
 
+    public static void expireOldAuthSessionCookie(RealmModel realm, UriInfo uriInfo, ClientConnection connection) {
+        logger.debugv("Expire {1} cookie .", AuthenticationSessionManager.AUTH_SESSION_ID);
+
+        String oldPath = getOldCookiePath(realm, uriInfo);
+        expireCookie(realm, AuthenticationSessionManager.AUTH_SESSION_ID, oldPath, true, connection);
+    }
+
     protected static String getIdentityCookiePath(RealmModel realm, UriInfo uriInfo) {
         return getRealmCookiePath(realm, uriInfo);
     }
 
     public static String getRealmCookiePath(RealmModel realm, UriInfo uriInfo) {
         URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName());
+        // KEYCLOAK-5270
+        return uri.getRawPath() + "/";
+    }
+
+    public static String getOldCookiePath(RealmModel realm, UriInfo uriInfo) {
+        URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getName());
         return uri.getRawPath();
     }
 
@@ -658,6 +687,7 @@ public class AuthenticationManager {
         AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, true, tokenString, session.getContext().getRequestHeaders());
         if (authResult == null) {
             expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
+            expireOldIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
             return null;
         }
         authResult.getSession().setLastSessionRefresh(Time.currentTime());
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java
index 83b23cf..6963153 100644
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationSessionManager.java
@@ -17,8 +17,6 @@
 
 package org.keycloak.services.managers;
 
-import javax.ws.rs.core.UriInfo;
-
 import org.jboss.logging.Logger;
 import org.keycloak.common.ClientConnection;
 import org.keycloak.models.ClientModel;
@@ -31,6 +29,13 @@ import org.keycloak.sessions.AuthenticationSessionModel;
 import org.keycloak.sessions.RootAuthenticationSessionModel;
 import org.keycloak.sessions.StickySessionEncoderProvider;
 
+import javax.ws.rs.core.UriInfo;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
@@ -38,6 +43,8 @@ public class AuthenticationSessionManager {
 
     public static final String AUTH_SESSION_ID = "AUTH_SESSION_ID";
 
+    public static final int AUTH_SESSION_LIMIT = 3;
+
     private static final Logger log = Logger.getLogger(AuthenticationSessionManager.class);
 
     private final KeycloakSession session;
@@ -64,14 +71,40 @@ public class AuthenticationSessionManager {
         return rootAuthSession;
     }
 
+    public RootAuthenticationSessionModel getCurrentRootAuthenticationSession(RealmModel realm) {
+        List<String> authSessionIds = getAuthSessionCookieIds(realm);
 
-    /**
-     * Returns ID of current authentication session if it exists, otherwise returns {@code null}.
-     * @param realm
-     * @return
-     */
-    public String getCurrentAuthenticationSessionId(RealmModel realm) {
-        return getAuthSessionCookieDecoded(realm);
+        return authSessionIds.stream().map(id -> {
+            SimpleEntry<String, String> entry = decodeAuthSessionId(id);
+            String sessionId = entry.getKey();
+
+            RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, sessionId);
+
+            if (rootAuthSession != null) {
+                reencodeAuthSessionCookie(sessionId, entry.getValue(), realm);
+                return rootAuthSession;
+            }
+
+            return null;
+        }).filter(authSession -> Objects.nonNull(authSession)).findFirst().orElse(null);
+    }
+
+    public UserSessionModel getUserSessionFromAuthCookie(RealmModel realm) {
+        List<String> authSessionIds = getAuthSessionCookieIds(realm);
+
+        return authSessionIds.stream().map(id -> {
+            SimpleEntry<String, String> entry = decodeAuthSessionId(id);
+            String sessionId = entry.getKey();
+
+            UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
+
+            if (userSession != null) {
+                reencodeAuthSessionCookie(sessionId, entry.getValue(), realm);
+                return userSession;
+            }
+
+            return null;
+        }).filter(authSession -> Objects.nonNull(authSession)).findFirst().orElse(null);
     }
 
 
@@ -81,13 +114,21 @@ public class AuthenticationSessionManager {
      * @return
      */
     public AuthenticationSessionModel getCurrentAuthenticationSession(RealmModel realm, ClientModel client, String tabId) {
-        String authSessionId = getAuthSessionCookieDecoded(realm);
+        List<String> authSessionIds = getAuthSessionCookieIds(realm);
 
-        if (authSessionId == null) {
-            return null;
-        }
+        return authSessionIds.stream().map(id -> {
+            SimpleEntry<String, String> entry = decodeAuthSessionId(id);
+            String sessionId = entry.getKey();
 
-        return getAuthenticationSessionByIdAndClient(realm, authSessionId, client, tabId);
+            AuthenticationSessionModel authSession = getAuthenticationSessionByIdAndClient(realm, sessionId, client, tabId);
+
+            if (authSession != null) {
+                reencodeAuthSessionCookie(sessionId, entry.getValue(), realm);
+                return authSession;
+            }
+
+            return null;
+        }).filter(authSession -> Objects.nonNull(authSession)).findFirst().orElse(null);
     }
 
 
@@ -105,28 +146,36 @@ public class AuthenticationSessionManager {
         log.debugf("Set AUTH_SESSION_ID cookie with value %s", encodedAuthSessionId);
     }
 
+    public SimpleEntry<String, String> decodeAuthSessionId(String authSessionId) {
+        log.debugf("Found AUTH_SESSION_ID cookie with value %s", authSessionId);
+        StickySessionEncoderProvider encoder = session.getProvider(StickySessionEncoderProvider.class);
+        String decodedAuthSessionId = encoder.decodeSessionId(authSessionId);
+        String reencoded = encoder.encodeSessionId(decodedAuthSessionId);
 
-    public String getAuthSessionCookieDecoded(RealmModel realm) {
-        String cookieVal = CookieHelper.getCookieValue(AUTH_SESSION_ID);
+        return new SimpleEntry(decodedAuthSessionId, reencoded);
+    }
 
-        if (cookieVal != null) {
-            log.debugf("Found AUTH_SESSION_ID cookie with value %s", cookieVal);
+    public void reencodeAuthSessionCookie(String decodedAuthSessionId, String reencodedAuthSessionId, RealmModel realm) {
+        if (!decodedAuthSessionId.equals(reencodedAuthSessionId)) {
+            log.debugf("Route changed. Will update authentication session cookie");
+            setAuthSessionCookie(decodedAuthSessionId, realm);
+        }
+    }
 
-            StickySessionEncoderProvider encoder = session.getProvider(StickySessionEncoderProvider.class);
-            String decodedAuthSessionId = encoder.decodeSessionId(cookieVal);
+    public List<String> getAuthSessionCookieIds(RealmModel realm) {
+        Set<String> cookiesVal = CookieHelper.getCookieValue(AUTH_SESSION_ID);
 
-            // Check if owner of this authentication session changed due to re-hashing (usually node failover or addition of new node)
-            String reencoded = encoder.encodeSessionId(decodedAuthSessionId);
-            if (!reencoded.equals(cookieVal)) {
-                log.debugf("Route changed. Will update authentication session cookie");
-                setAuthSessionCookie(decodedAuthSessionId, realm);
-            }
+        if (cookiesVal.size() > 1) {
+            AuthenticationManager.expireOldAuthSessionCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
+        }
 
-            return decodedAuthSessionId;
-        } else {
+        List<String> authSessionIds = cookiesVal.stream().limit(AUTH_SESSION_LIMIT).collect(Collectors.toList());
+
+        if (authSessionIds.isEmpty()) {
             log.debugf("Not found AUTH_SESSION_ID cookie");
-            return null;
         }
+
+        return authSessionIds;
     }
 
 
@@ -156,5 +205,4 @@ public class AuthenticationSessionManager {
         RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, authSessionId);
         return rootAuthSession==null ? null : rootAuthSession.getAuthenticationSession(client, tabId);
     }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
index 476022c..bd56400 100644
--- a/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserSessionCrossDCManager.java
@@ -17,14 +17,15 @@
 
 package org.keycloak.services.managers;
 
-import java.util.Map;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.List;
+import java.util.Objects;
 
 import org.jboss.logging.Logger;
 import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserSessionModel;
-import org.keycloak.sessions.CommonClientSessionModel;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -60,14 +61,24 @@ public class UserSessionCrossDCManager {
 
 
     // Just check if userSession also exists on remoteCache. It can happen that logout happened on 2nd DC and userSession is already removed on remoteCache and this DC wasn't yet notified
-    public UserSessionModel getUserSessionIfExistsRemotely(RealmModel realm, String id) {
-        UserSessionModel userSession = kcSession.sessions().getUserSession(realm, id);
+    public UserSessionModel getUserSessionIfExistsRemotely(AuthenticationSessionManager asm, RealmModel realm) {
+        List<String> sessionIds = asm.getAuthSessionCookieIds(realm);
 
-        // This will remove userSession "locally" if it doesn't exists on remoteCache
-        kcSession.sessions().getUserSessionWithPredicate(realm, id, false, (UserSessionModel userSession2) -> {
-            return userSession2 == null;
-        });
+        return sessionIds.stream().map(id -> {
+            SimpleEntry<String, String> entry = asm.decodeAuthSessionId(id);
+            String sessionId = entry.getKey();
+
+            // This will remove userSession "locally" if it doesn't exists on remoteCache
+            kcSession.sessions().getUserSessionWithPredicate(realm, sessionId, false, (UserSessionModel userSession2) -> userSession2 == null);
+
+            UserSessionModel userSession = kcSession.sessions().getUserSession(realm, sessionId);
+
+            if (userSession != null) {
+                asm.reencodeAuthSessionCookie(sessionId, entry.getValue(), realm);
+                return userSession;
+            }
 
-        return kcSession.sessions().getUserSession(realm, id);
+            return null;
+        }).filter(userSession -> Objects.nonNull(userSession)).findFirst().orElse(null);
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index f517c7d..1c9ff23 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -322,7 +322,7 @@ public class LoginActionsService {
                                          @QueryParam(Constants.TAB_ID) String tabId,
                                          @QueryParam(Constants.KEY) String key) {
         if (key != null) {
-            return handleActionToken(authSessionId, key, execution, clientId, tabId);
+            return handleActionToken(key, execution, clientId, tabId);
         }
 
         event.event(EventType.RESET_PASSWORD);
@@ -422,10 +422,10 @@ public class LoginActionsService {
                                        @QueryParam("execution") String execution,
                                        @QueryParam("client_id") String clientId,
                                        @QueryParam(Constants.TAB_ID) String tabId) {
-        return handleActionToken(authSessionId, key, execution, clientId, tabId);
+        return handleActionToken(key, execution, clientId, tabId);
     }
 
-    protected <T extends JsonWebToken & ActionTokenKeyModel> Response handleActionToken(String authSessionId, String tokenString, String execution, String clientId, String tabId) {
+    protected <T extends JsonWebToken & ActionTokenKeyModel> Response handleActionToken(String tokenString, String execution, String clientId, String tabId) {
         T token;
         ActionTokenHandler<T> handler;
         ActionTokenContext<T> tokenContext;
@@ -442,7 +442,6 @@ public class LoginActionsService {
         AuthenticationSessionManager authenticationSessionManager = new AuthenticationSessionManager(session);
         if (client != null) {
             session.getContext().setClient(client);
-            authSessionId = authSessionId == null ? authenticationSessionManager.getAuthSessionCookieDecoded(realm) : authSessionId;
             authSession = authenticationSessionManager.getCurrentAuthenticationSession(realm, client, tabId);
         }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java b/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java
index bb4097f..e61818a 100644
--- a/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java
+++ b/services/src/main/java/org/keycloak/services/resources/SessionCodeChecks.java
@@ -18,6 +18,10 @@
 package org.keycloak.services.resources;
 
 import java.net.URI;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -73,7 +77,6 @@ public class SessionCodeChecks {
     private final String flowPath;
     private final String authSessionId;
 
-
     public SessionCodeChecks(RealmModel realm, UriInfo uriInfo, HttpRequest request, ClientConnection clientConnection, KeycloakSession session, EventBuilder event,
                              String authSessionId, String code, String execution, String clientId, String tabId, String flowPath) {
         this.realm = realm;
@@ -175,28 +178,22 @@ public class SessionCodeChecks {
         }
 
         // See if we are already authenticated and userSession with same ID exists.
-        String sessionId = authSessionManager.getCurrentAuthenticationSessionId(realm);
-        RootAuthenticationSessionModel existingRootAuthSession = null;
-        if (sessionId != null) {
-            UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
-            if (userSession != null) {
-
-                LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class).setAuthenticationSession(authSession)
-                        .setSuccess(Messages.ALREADY_LOGGED_IN);
+        UserSessionModel userSession = authSessionManager.getUserSessionFromAuthCookie(realm);
 
-                if (client == null) {
-                    loginForm.setAttribute(Constants.SKIP_LINK, true);
-                }
+        if (userSession != null) {
+            LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class).setAuthenticationSession(authSession)
+                    .setSuccess(Messages.ALREADY_LOGGED_IN);
 
-                response = loginForm.createInfoPage();
-                return null;
+            if (client == null) {
+                loginForm.setAttribute(Constants.SKIP_LINK, true);
             }
 
-
-            existingRootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realm, sessionId);
+            response = loginForm.createInfoPage();
+            return null;
         }
 
         // Otherwise just try to restart from the cookie
+        RootAuthenticationSessionModel existingRootAuthSession = authSessionManager.getCurrentRootAuthenticationSession(realm);
         response = restartAuthenticationSessionFromCookie(existingRootAuthSession);
         return null;
     }
@@ -433,5 +430,4 @@ public class SessionCodeChecks {
         return new AuthenticationFlowURLHelper(session, realm, uriInfo)
                 .showPageExpired(authSession);
     }
-
 }
diff --git a/services/src/main/java/org/keycloak/services/util/CookieHelper.java b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
index 2005695..b31f9f7 100755
--- a/services/src/main/java/org/keycloak/services/util/CookieHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/CookieHelper.java
@@ -17,19 +17,20 @@
 
 package org.keycloak.services.util;
 
-import java.net.URI;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
+import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.common.util.ServerCookie;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.services.managers.AuthenticationManager;
 
 import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
+
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -37,6 +38,8 @@ import javax.ws.rs.core.UriInfo;
  */
 public class CookieHelper {
 
+    private static final Logger logger = Logger.getLogger(CookieHelper.class);
+
     /**
      * Set a response cookie.  This solely exists because JAX-RS 1.1 does not support setting HttpOnly cookies
      *
@@ -58,10 +61,36 @@ public class CookieHelper {
     }
 
 
-    public static String getCookieValue(String name) {
+    public static Set<String> getCookieValue(String name) {
         HttpHeaders headers = ResteasyProviderFactory.getContextData(HttpHeaders.class);
+
+        Set<String> cookiesVal = new HashSet<>();
+
+        // check for cookies in the request headers
+        List<String> cookieHeader = headers.getRequestHeaders().get(HttpHeaders.COOKIE);
+        if (cookieHeader != null) {
+            logger.debugv("{1} cookie found in the request's header", name);
+            cookieHeader.stream().map(s -> parseCookie(s, name)).forEach(cookiesVal::addAll);
+        }
+
+        // get cookies from the cookie field
         Cookie cookie = headers.getCookies().get(name);
-        return cookie != null ? cookie.getValue() : null;
+        if (cookie != null) {
+            logger.debugv("{1} cookie found in the cookie's field", name);
+            cookiesVal.add(cookie.getValue());
+        }
+
+
+        return cookiesVal;
     }
 
+
+    public static Set<String> parseCookie(String cookieHeader, String name) {
+        String parts[] = cookieHeader.split("[;,]");
+
+        Set<String> cookies = Arrays.stream(parts).filter(part -> part.startsWith(name + "=")).map(part ->
+                part.substring(part.indexOf('=') + 1)).collect(Collectors.toSet());
+
+        return cookies;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java
new file mode 100644
index 0000000..7cf724b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cookies/CookiesPathTest.java
@@ -0,0 +1,292 @@
+package org.keycloak.testsuite.cookies;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HttpCoreContext;
+import org.hamcrest.Matchers;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.AccountRoles;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.AuthenticationSessionManager;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.ActionURIUtils;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.URLUtils;
+import org.keycloak.testsuite.util.UserBuilder;
+import org.openqa.selenium.Cookie;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
+ */
+public class CookiesPathTest extends AbstractKeycloakTest {
+
+    @Page
+    protected LoginPage loginPage;
+
+    public static final String AUTH_SESSION_VALUE = "1869c345-2f90-4724-936d-a1a1ef41dea7";
+
+    public static final String AUTH_SESSION_VALUE_NODE = "1869c345-2f90-4724-936d-a1a1ef41dea7.host";
+
+    public static final String OLD_COOKIE_PATH = "/auth/realms/foo";
+
+    public static final String KC_RESTART = "KC_RESTART";
+
+    @Test
+    public void testCookiesPath() {
+        // navigate to "/realms/foo/account" and remove cookies in the browser for the current path
+        // first access to the path means there are no cookies being sent
+        // we are redirected to login page and Keycloak sets cookie's path to "/auth/realms/foo/"
+        deleteAllCookiesForRealm("foo");
+
+        Assert.assertTrue("There shouldn't be any cookies sent!", driver.manage().getCookies().isEmpty());
+
+        // refresh the page and cookies are sent within the request
+        driver.navigate().refresh();
+
+        Set<Cookie> cookies = driver.manage().getCookies();
+        Assert.assertTrue("There should be cookies sent!", cookies.size() > 0);
+        // check cookie's path, for some reason IE adds extra slash to the beginning of the path
+        cookies.stream().forEach(cookie -> Assert.assertThat(cookie.getPath(), Matchers.endsWith("/auth/realms/foo/")));
+
+        // now navigate to realm which name overlaps the first realm and delete cookies for that realm (foobar)
+        deleteAllCookiesForRealm("foobar");
+
+        // cookies shouldn't be sent for the first access to /realms/foobar/account
+        // At this moment IE would sent cookies for /auth/realms/foo without the fix
+        cookies = driver.manage().getCookies();
+        Assert.assertTrue("There shouldn't be any cookies sent!", cookies.isEmpty());
+
+        // refresh the page and check if correct cookies were sent
+        driver.navigate().refresh();
+        cookies = driver.manage().getCookies();
+
+        Assert.assertTrue("There should be cookies sent!", cookies.size() > 0);
+        // check cookie's path, for some reason IE adds extra slash to the beginning of the path
+        cookies.stream().forEach(cookie -> Assert.assertThat(cookie.getPath(), Matchers.endsWith("/auth/realms/foobar/")));
+
+        // lets back to "/realms/foo/account" to test the cookies for "foo" realm are still there and haven't been (correctly) sent to "foobar"
+        URLUtils.navigateToUri( oauth.AUTH_SERVER_ROOT + "/realms/foo/account", true);
+
+        cookies = driver.manage().getCookies();
+        Assert.assertTrue("There should be cookies sent!", cookies.size() > 0);
+        cookies.stream().forEach(cookie -> Assert.assertThat(cookie.getPath(), Matchers.endsWith("/auth/realms/foo/")));
+    }
+
+    @Test
+    public void testMultipleCookies() throws IOException {
+        String requestURI = oauth.AUTH_SERVER_ROOT + "/realms/foo/account";
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_YEAR, 1);
+
+        // create old cookie with wrong path
+        BasicClientCookie wrongCookie = new BasicClientCookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE);
+        wrongCookie.setDomain("localhost");
+        wrongCookie.setPath(OLD_COOKIE_PATH);
+        wrongCookie.setExpiryDate(calendar.getTime());
+
+        // obtain new cookies
+        CookieStore cookieStore = getCorrectCookies(requestURI);
+        cookieStore.addCookie(wrongCookie);
+
+        Assert.assertThat(cookieStore.getCookies(), Matchers.hasSize(3));
+
+        CloseableHttpResponse response = login(requestURI, cookieStore);
+        response.close();
+
+        // old cookie has been removed
+        // now we have AUTH_SESSION_ID, KEYCLOAK_IDENTITY, KEYCLOAK_SESSION, OAuth_Token_Request_State
+        Assert.assertThat(cookieStore.getCookies(), Matchers.hasSize(4));
+
+        // does each cookie's path end with "/"
+        cookieStore.getCookies().stream().filter(c -> !"OAuth_Token_Request_State".equals(c.getName()))
+                .map(c -> c.getPath()).forEach(path ->Assert.assertThat(path, Matchers.endsWith("/")));
+
+        // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value
+        String authSessionId = cookieStore.getCookies().stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionId = cookieStore.getCookies().stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionSuffix = KCSessionId.split("/")[2];
+        Assert.assertThat(authSessionId, Matchers.containsString(KCSessionSuffix));
+    }
+
+    @Test
+    public void testOldCookieWithWrongPath() {
+        Cookie wrongCookie = new Cookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE,
+                null, OLD_COOKIE_PATH, null, false, true);
+
+        deleteAllCookiesForRealm("foo");
+
+        // add old cookie with wrong path
+        driver.manage().addCookie(wrongCookie);
+        Set<Cookie> cookies = driver.manage().getCookies();
+        Assert.assertThat(cookies, Matchers.hasSize(1));
+
+        oauth.realm("foo").redirectUri(OAuthClient.AUTH_SERVER_ROOT + "/realms/foo/account").clientId("account").openLoginForm();
+
+        loginPage.login("foo", "password");
+
+        // old cookie has been removed and new cookies have been added
+        cookies = driver.manage().getCookies();
+        Assert.assertThat(cookies, Matchers.hasSize(3));
+
+        // does each cookie's path end with "/"
+        cookies.stream().map(c -> c.getPath()).forEach(path ->
+                Assert.assertThat(path, Matchers.endsWith("/")));
+
+        // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value
+        String authSessionId = cookies.stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionId = cookies.stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionSuffix = KCSessionId.split("/")[2];
+        Assert.assertThat(authSessionId, Matchers.containsString(KCSessionSuffix));
+    }
+
+    @Test
+    public void testOldCookieWithNodeInValue() throws IOException {
+        String requestURI = oauth.AUTH_SERVER_ROOT + "/realms/foo/account";
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_YEAR, 1);
+
+        // create old cookie with wrong path
+        BasicClientCookie wrongCookie = new BasicClientCookie(AuthenticationSessionManager.AUTH_SESSION_ID, AUTH_SESSION_VALUE_NODE);
+        wrongCookie.setDomain("localhost");
+        wrongCookie.setPath(OLD_COOKIE_PATH);
+        wrongCookie.setExpiryDate(calendar.getTime());
+
+        // obtain new cookies
+        CookieStore cookieStore = getCorrectCookies(requestURI);
+        cookieStore.addCookie(wrongCookie);
+
+        Assert.assertThat(cookieStore.getCookies(), Matchers.hasSize(3));
+
+        CloseableHttpResponse response = login(requestURI, cookieStore);
+        response.close();
+
+        // old cookie has been removed
+        // now we have AUTH_SESSION_ID, KEYCLOAK_IDENTITY, KEYCLOAK_SESSION, OAuth_Token_Request_State
+        Assert.assertThat(cookieStore.getCookies(), Matchers.hasSize(4));
+
+        // does each cookie's path end with "/"
+        cookieStore.getCookies().stream().filter(c -> !"OAuth_Token_Request_State".equals(c.getName()))
+                .map(c -> c.getPath()).forEach(path ->Assert.assertThat(path, Matchers.endsWith("/")));
+
+        // KEYCLOAK_SESSION should end by AUTH_SESSION_ID value
+        String authSessionId = cookieStore.getCookies().stream().filter(c -> "AUTH_SESSION_ID".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionId = cookieStore.getCookies().stream().filter(c -> "KEYCLOAK_SESSION".equals(c.getName())).findFirst().get().getValue();
+        String KCSessionSuffix = KCSessionId.split("/")[2];
+        Assert.assertThat(authSessionId, Matchers.containsString(KCSessionSuffix));
+    }
+
+    /**
+     * Add two realms which names are overlapping i.e foo and foobar
+     * @param testRealms
+     */
+    @Override
+    public void addTestRealms(List<RealmRepresentation> testRealms) {
+        RealmBuilder foo = RealmBuilder.create().name("foo");
+        foo.user(UserBuilder.create().username("foo").password("password").role("account", AdminRoles.ADMIN)
+                .role("account", AccountRoles.MANAGE_ACCOUNT).role("account", AccountRoles.VIEW_PROFILE).role("account", AccountRoles.MANAGE_ACCOUNT_LINKS));
+        testRealms.add(foo.build());
+
+        RealmBuilder foobar = RealmBuilder.create().name("foobar");
+        foo.user(UserBuilder.create().username("foobar").password("password").role("account", AdminRoles.ADMIN)
+                .role("account", AccountRoles.MANAGE_ACCOUNT).role("account", AccountRoles.VIEW_PROFILE).role("account", AccountRoles.MANAGE_ACCOUNT_LINKS));
+        testRealms.add(foobar.build());
+    }
+
+    private CloseableHttpResponse sendRequest(HttpRequestBase request, CookieStore cookieStore, HttpCoreContext localContext) throws IOException {
+        CloseableHttpClient c = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).setRedirectStrategy(new LaxRedirectStrategy()).build();
+
+        CloseableHttpResponse response = c.execute(request, localContext);
+
+        return response;
+    }
+
+    private CookieStore getCorrectCookies(String uri) throws IOException {
+        CookieStore cookieStore = new BasicCookieStore();
+
+        HttpGet request = new HttpGet(uri);
+        CloseableHttpResponse response = sendRequest(request, new BasicCookieStore(), new HttpCoreContext());
+
+        for (org.apache.http.Header h: response.getHeaders("Set-Cookie")) {
+            if (h.getValue().contains(AuthenticationSessionManager.AUTH_SESSION_ID)) {
+                cookieStore.addCookie(parseCookie(h.getValue(), AuthenticationSessionManager.AUTH_SESSION_ID));
+            } else if (h.getValue().contains(KC_RESTART)) {
+                cookieStore.addCookie(parseCookie(h.getValue(), KC_RESTART));
+            }
+        }
+
+        response.close();
+
+        return cookieStore;
+    }
+
+    private BasicClientCookie parseCookie(String line, String name) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.DAY_OF_YEAR, 1);
+
+        String path = "";
+        String value = "";
+
+        for (String s: line.split(";")) {
+            if (s.contains(name)) {
+                String[] split = s.split("=");
+                value = split[1];
+            } else if (s.contains("Path")) {
+                String[] split = s.split("=");
+                path = split[1];
+            }
+        }
+
+        BasicClientCookie c = new BasicClientCookie(name, value);
+        c.setExpiryDate(calendar.getTime());
+        c.setDomain("localhost");
+        c.setPath(path);
+
+        return c;
+    }
+
+    private CloseableHttpResponse login(String requestURI, CookieStore cookieStore) throws IOException {
+        HttpCoreContext httpContext = new HttpCoreContext();
+        HttpGet request = new HttpGet(requestURI);
+
+        // send an initial request, we are redirected to login page
+        CloseableHttpResponse response = sendRequest(request, cookieStore, httpContext);
+        String s = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
+        response.close();
+        String action = ActionURIUtils.getActionURIFromPageSource(s);
+
+        // send credentials to login form
+        HttpPost post = new HttpPost(action);
+        List<NameValuePair> params = new LinkedList<>();
+        params.add(new BasicNameValuePair("username", "foo"));
+        params.add(new BasicNameValuePair("password", "password"));
+
+        post.setHeader("Content-Type", "application/x-www-form-urlencoded");
+        post.setEntity(new UrlEncodedFormEntity(params));
+
+        return sendRequest(post, cookieStore, httpContext);
+    }
+}