Remove legacy cookies

Closes #16770

Signed-off-by: stianst <stianst@gmail.com>
Signed-off-by: Jon Koops <jonkoops@gmail.com>
Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Stian Thorgersen 2024-08-15 15:27:38 +02:00 committed by GitHub
parent 708a6898db
commit 310824cc2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 197 additions and 195 deletions

View file

@ -119,3 +119,13 @@ It helps to find performance bottlenecks, determine the cause of application fai
The support is in preview mode, and we would be happy to obtain any feedback.
For more information, see the link:{tracingguide_link}[{tracingguide_name}] guide.
= Removal of legacy cookies
Keycloak no longer sends `_LEGACY` cookies, which where introduced as a work-around to older browsers not supporting
the `SameSite` flag on cookies.
The `_LEGACY` cookies also served another purpose, which was to allow login from an insecure context. Although, this is
not recommended at all in production deployments of Keycloak, it is fairly frequent to access Keycloak over `http` outside
of `localhost`. As an alternative to the `_LEGACY` cookies Keycloak now doesn't set the `secure` flag and sets `SameSite=Lax`
instead of `SameSite=None` when it detects an insecure context is used.

View file

@ -4,6 +4,12 @@ import jakarta.annotation.Nullable;
public final class CookieType {
public static final CookieType[] OLD_UNUSED_COOKIES = new CookieType[] {
CookieType.create("AUTH_SESSION_ID_LEGACY").build(),
CookieType.create("KEYCLOAK_IDENTITY_LEGACY").build(),
CookieType.create("KEYCLOAK_SESSION_LEGACY").build()
};
public static final CookieType AUTH_DETACHED = CookieType.create("KC_STATE_CHECKER")
.scope(CookieScope.INTERNAL)
.build();
@ -16,12 +22,10 @@ public final class CookieType {
public static final CookieType AUTH_SESSION_ID = CookieType.create("AUTH_SESSION_ID")
.scope(CookieScope.FEDERATION)
.defaultMaxAge(CookieMaxAge.SESSION)
.supportSameSiteLegacy()
.build();
public static final CookieType IDENTITY = CookieType.create("KEYCLOAK_IDENTITY")
.scope(CookieScope.FEDERATION)
.supportSameSiteLegacy()
.build();
public static final CookieType LOCALE = CookieType.create("KEYCLOAK_LOCALE")
@ -36,7 +40,6 @@ public final class CookieType {
public static final CookieType SESSION = CookieType.create("KEYCLOAK_SESSION")
.scope(CookieScope.FEDERATION_JS)
.supportSameSiteLegacy()
.build();
public static final CookieType WELCOME_CSRF = CookieType.create("WELCOME_STATE_CHECKER")
@ -45,15 +48,13 @@ public final class CookieType {
.build();
private final String name;
private final String sameSiteLegacyName;
private final CookiePath path;
private final CookieScope scope;
private final Integer defaultMaxAge;
private CookieType(String name, boolean supportsSameSiteLegacy, CookiePath path, CookieScope scope, @Nullable Integer defaultMaxAge) {
private CookieType(String name, CookiePath path, CookieScope scope, @Nullable Integer defaultMaxAge) {
this.name = name;
this.sameSiteLegacyName = supportsSameSiteLegacy ? name + "_LEGACY" : null;
this.path = path;
this.scope = scope;
this.defaultMaxAge = defaultMaxAge;
@ -67,16 +68,6 @@ public final class CookieType {
return name;
}
@Deprecated
public boolean supportsSameSiteLegacy() {
return sameSiteLegacyName != null;
}
@Deprecated
public String getSameSiteLegacyName() {
return sameSiteLegacyName;
}
public CookiePath getPath() {
return path;
}
@ -92,7 +83,6 @@ public final class CookieType {
private static class CookieTypeBuilder {
private String name;
private boolean supportSameSiteLegacy = false;
private CookiePath path = CookiePath.REALM;
private CookieScope scope = CookieScope.INTERNAL;
private Integer defaultMaxAge;
@ -111,18 +101,13 @@ public final class CookieType {
return this;
}
CookieTypeBuilder supportSameSiteLegacy() {
this.supportSameSiteLegacy = true;
return this;
}
CookieTypeBuilder defaultMaxAge(int defaultMaxAge) {
this.defaultMaxAge = defaultMaxAge;
return this;
}
CookieType build() {
return new CookieType(name, supportSameSiteLegacy, path, scope, defaultMaxAge);
return new CookieType(name, path, scope, defaultMaxAge);
}
}

View file

@ -1,39 +0,0 @@
package org.keycloak.cookie;
import jakarta.ws.rs.core.NewCookie;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.RealmModel;
import java.net.URI;
class CookieSecureResolver {
private final KeycloakContext context;
private final boolean sameSiteLegacyEnabled;
CookieSecureResolver(KeycloakContext context, boolean sameSiteLegacyEnabled) {
this.context = context;
this.sameSiteLegacyEnabled = sameSiteLegacyEnabled;
}
boolean resolveSecure(NewCookie.SameSite sameSite) {
// Due to cookies with SameSite=none secure context is always required when same-site legacy cookies are disabled
if (!sameSiteLegacyEnabled) {
return true;
} else {
// SameSite=none requires secure context
if (NewCookie.SameSite.NONE.equals(sameSite)) {
return true;
}
URI requestUri = context.getUri().getRequestUri();
RealmModel realm = context.getRealm();
if (realm != null && realm.getSslRequired().isRequired(requestUri.getHost())) {
return true;
}
return false;
}
}
}

View file

@ -12,25 +12,27 @@ public class DefaultCookieProvider implements CookieProvider {
private final KeycloakContext context;
private CookiePathResolver pathResolver;
private final CookiePathResolver pathResolver;
private CookieSecureResolver secureResolver;
private final boolean secure;
private final Map<String, Cookie> cookies;
private final boolean sameSiteLegacyEnabled;
public DefaultCookieProvider(KeycloakContext context, boolean sameSiteLegacyEnabled) {
public DefaultCookieProvider(KeycloakContext context) {
this.context = context;
this.cookies = context.getRequestHeaders().getCookies();
this.pathResolver = new CookiePathResolver(context);
this.secureResolver = new CookieSecureResolver(context, sameSiteLegacyEnabled);
this.sameSiteLegacyEnabled = sameSiteLegacyEnabled;
this.secure = SecureContextResolver.isSecureContext(context.getUri().getRequestUri());
if (logger.isTraceEnabled()) {
String cookieNames = String.join(", ", this.cookies.keySet());
logger.tracef("Path: %s, cookies: %s", context.getUri().getRequestUri().getRawPath(), cookieNames);
logger.tracef("Received cookies: %s, path: %s", String.join(", ", this.cookies.keySet()), context.getUri().getRequestUri().getRawPath());
}
if (!secure) {
logger.warnf("Non-secure context detected; cookies are not secured, and will not be available in cross-origin POST requests");
}
expireOldUnusedCookies();
}
@Override
@ -46,7 +48,10 @@ public class DefaultCookieProvider implements CookieProvider {
public void set(CookieType cookieType, String value, int maxAge) {
String name = cookieType.getName();
NewCookie.SameSite sameSite = cookieType.getScope().getSameSite();
boolean secure = secureResolver.resolveSecure(sameSite);
if (NewCookie.SameSite.NONE.equals(sameSite) && !secure) {
sameSite = NewCookie.SameSite.LAX;
}
String path = pathResolver.resolvePath(cookieType);
boolean httpOnly = cookieType.getScope().isHttpOnly();
@ -63,63 +68,17 @@ public class DefaultCookieProvider implements CookieProvider {
context.getHttpResponse().setCookieIfAbsent(newCookie);
logger.tracef("Setting cookie: name: %s, path: %s, same-site: %s, secure: %s, http-only: %s, max-age: %d", name, path, sameSite, secure, httpOnly, maxAge);
setSameSiteLegacy(cookieType, value, maxAge);
}
private void setSameSiteLegacy(CookieType cookieType, String value, int maxAge) {
if (sameSiteLegacyEnabled && cookieType.supportsSameSiteLegacy()) {
String legacyName = cookieType.getSameSiteLegacyName();
boolean legacySecure = secureResolver.resolveSecure(null);
String path = pathResolver.resolvePath(cookieType);
boolean httpOnly = cookieType.getScope().isHttpOnly();
NewCookie legacyCookie = new NewCookie.Builder(legacyName)
.version(1)
.value(value)
.maxAge(maxAge)
.path(path)
.secure(legacySecure)
.httpOnly(httpOnly)
.build();
context.getHttpResponse().setCookieIfAbsent(legacyCookie);
logger.tracef("Setting legacy cookie: name: %s, path: %s, same-site: %s, secure: %s, http-only: %s, max-age: %d", legacyName, path, null, legacySecure, httpOnly, maxAge);
} else if (cookieType.supportsSameSiteLegacy()) {
expireSameSiteLegacy(cookieType);
}
}
@Override
public String get(CookieType cookieType) {
Cookie cookie = cookies.get(cookieType.getName());
if (cookie == null) {
cookie = getSameSiteLegacyCookie(cookieType);
}
return cookie != null ? cookie.getValue() : null;
}
private Cookie getSameSiteLegacyCookie(CookieType cookieType) {
if (cookieType.supportsSameSiteLegacy()) {
return cookies.get(cookieType.getSameSiteLegacyName());
} else {
return null;
}
}
@Override
public void expire(CookieType cookieType) {
expire(cookieType.getName(), cookieType);
expireSameSiteLegacy(cookieType);
}
private void expireSameSiteLegacy(CookieType cookieType) {
if (cookieType.supportsSameSiteLegacy()) {
expire(cookieType.getSameSiteLegacyName(), cookieType);
}
}
private void expire(String cookieName, CookieType cookieType) {
String cookieName = cookieType.getName();
Cookie cookie = cookies.get(cookieName);
if (cookie != null) {
String path = pathResolver.resolvePath(cookieType);
@ -135,6 +94,12 @@ public class DefaultCookieProvider implements CookieProvider {
}
}
private void expireOldUnusedCookies() {
for (CookieType cookieType : CookieType.OLD_UNUSED_COOKIES) {
expire(cookieType);
}
}
@Override
public void close() {
}

View file

@ -3,24 +3,16 @@ package org.keycloak.cookie;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import java.util.List;
public class DefaultCookieProviderFactory implements CookieProviderFactory {
private static final String SAME_SITE_LEGACY_KEY = "sameSiteLegacy";
private boolean sameSiteLegacyEnabled;
@Override
public CookieProvider create(KeycloakSession session) {
return new DefaultCookieProvider(session.getContext(), sameSiteLegacyEnabled);
return new DefaultCookieProvider(session.getContext());
}
@Override
public void init(Config.Scope config) {
sameSiteLegacyEnabled = config.getBoolean(SAME_SITE_LEGACY_KEY, true);
}
@Override
@ -36,15 +28,4 @@ public class DefaultCookieProviderFactory implements CookieProviderFactory {
return "default";
}
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name(SAME_SITE_LEGACY_KEY)
.type("boolean")
.helpText("Adds legacy cookies without SameSite parameter")
.defaultValue(true)
.add()
.build();
}
}

View file

@ -0,0 +1,46 @@
package org.keycloak.cookie;
import java.net.URI;
class SecureContextResolver {
/**
* Determines if a URI is potentially trustworthy, meaning a user agent can generally trust it to deliver data securely.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts">MDN Web Docs Secure Contexts</a>
* @see <a href="https://w3c.github.io/webappsec-secure-contexts/#algorithms">W3C Secure Contexts specification Is origin potentially trustworthy?</a>
* @param uri The URI to check.
* @return Whether the URI can be considered potentially trustworthy.
*/
static boolean isSecureContext(URI uri) {
if (uri.getScheme().equals("https")) {
return true;
}
String host = uri.getHost();
if (host == null) {
return false;
}
// The host matches a CIDR notation of ::1/128
if (host.equals("[::1]") || host.equals("[0000:0000:0000:0000:0000:0000:0000:0001]")) {
return true;
}
// The host matches a CIDR notation of 127.0.0.0/8
if (host.matches("127.\\d{1,3}.\\d{1,3}.\\d{1,3}")) {
return true;
}
if (host.equals("localhost") || host.equals("localhost.")) {
return true;
}
if (host.endsWith(".localhost") || host.endsWith(".localhost.")) {
return true;
}
return false;
}
}

View file

@ -108,14 +108,9 @@
function getSessionCookie() {
const cookie = getCookieByName("KEYCLOAK_SESSION");
if (cookie !== null) {
return cookie;
}
return getCookieByName("KEYCLOAK_SESSION_LEGACY");
}
function getCookieByName(name) {
for (const cookie of document.cookie.split(";")) {
const [key, value] = cookie.split("=").map((value) => value.trim());

View file

@ -0,0 +1,58 @@
package org.keycloak.cookie;
import org.junit.Assert;
import org.junit.Test;
import java.net.URI;
import java.net.URISyntaxException;
public class SecureContextResolverTest {
@Test
public void testHttps() {
assertSecureContext("https://127.0.0.1", true);
assertSecureContext("https://something", true);
}
@Test
public void testIp4() {
assertSecureContext("http://127.0.0.1", true);
assertSecureContext("http://127.0.0.128", true);
assertSecureContext("http://127.0.0.255", true);
assertSecureContext("http://127.0.0.256", false);
assertSecureContext("http://127.0.1.1", true);
assertSecureContext("http://127.254.232.123", true);
assertSecureContext("http://127.256.232.123", false);
assertSecureContext("http://10.0.0.10", false);
assertSecureContext("http://127.0.0.1.nip.io", false);
}
@Test
public void testIp6() {
assertSecureContext("http://[::1]", true);
assertSecureContext("http://[0000:0000:0000:0000:0000:0000:0000:0001]", true);
assertSecureContext("http://[::2]", false);
assertSecureContext("http://[2001:0000:130F:0000:0000:09C0:876A:130B]", false);
assertSecureContext("http://::1", false);
}
@Test
public void testLocalhost() {
assertSecureContext("http://localhost", true);
assertSecureContext("http://localhost.", true);
assertSecureContext("http://localhostn", false);
assertSecureContext("http://test.localhost", true);
assertSecureContext("http://test.localhost.", true);
assertSecureContext("http://test.localhostn", false);
assertSecureContext("http://test.localhost.not", false);
}
void assertSecureContext(String url, boolean expectedSecureContext) {
try {
Assert.assertEquals(expectedSecureContext, SecureContextResolver.isSecureContext(new URI(url)));
} catch (URISyntaxException e) {
Assert.fail(e.getMessage());
}
}
}

View file

@ -175,7 +175,7 @@ public class SimpleUndertowLoadBalancer {
private HttpHandler createHandler() throws Exception {
// TODO: configurable options if needed
String[] sessionIds = {CookieType.AUTH_SESSION_ID.getName(), CookieType.AUTH_SESSION_ID.getSameSiteLegacyName()};
String[] sessionIds = {CookieType.AUTH_SESSION_ID.getName()};
int connectionsPerThread = 20;
int problemServerRetry = 5; // In case of unavailable node, we will try to ping him every 5 seconds to check if it's back
int maxTime = 3600000; // 1 hour for proxy request timeout, so we can debug the backend keycloak servers

View file

@ -1819,9 +1819,7 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
Cookie identityCookie = driver.manage().getCookieNamed(CookieType.IDENTITY.getName());
Assert.assertNotNull(identityCookie);
driver.manage().deleteCookieNamed(CookieType.AUTH_SESSION_ID.getName());
driver.manage().deleteCookieNamed(CookieType.AUTH_SESSION_ID.getSameSiteLegacyName());
driver.manage().addCookie(new Cookie(CookieType.AUTH_SESSION_ID.getName(), "invalid-value", identityCookie.getPath()));
driver.manage().addCookie(new Cookie(CookieType.AUTH_SESSION_ID.getSameSiteLegacyName(), "invalid-value", identityCookie.getPath()));
// go back to the app page, re-login should work with the invalid cookie
testRealmSAMLPostLoginPage.navigateTo();

View file

@ -97,10 +97,8 @@ public class KcOidcBrokerLogoutTest extends AbstractKcOidcBrokerLogoutTest {
String idToken = response.getIdToken();
// simulate browser restart by deleting an identity cookie
log.debugf("Deleting %s and %s cookies", CookieType.IDENTITY.getName(),
CookieType.IDENTITY.getSameSiteLegacyName());
log.debugf("Deleting %s cookie", CookieType.IDENTITY.getName());
driver.manage().deleteCookieNamed(CookieType.IDENTITY.getName());
driver.manage().deleteCookieNamed(CookieType.IDENTITY.getSameSiteLegacyName());
AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), bc.getUserLogin());
AccountHelper.logout(adminClient.realm(bc.providerRealmName()), bc.getUserLogin());

View file

@ -113,9 +113,6 @@ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverCl
public static String getAuthSessionCookieValue(WebDriver driver) {
Cookie authSessionCookie = driver.manage().getCookieNamed(CookieType.AUTH_SESSION_ID.getName());
if (authSessionCookie == null) {
authSessionCookie = driver.manage().getCookieNamed(CookieType.AUTH_SESSION_ID.getSameSiteLegacyName());
}
Assert.assertNotNull(authSessionCookie);
return authSessionCookie.getValue();
}

View file

@ -40,6 +40,7 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest {
@Test
public void testCookieDefaults() {
Response response = testing.server("master").runWithResponse(session -> {
CookieProvider cookies = session.getProvider(CookieProvider.class);
cookies.set(CookieType.AUTH_SESSION_ID, "my-auth-session-id");
cookies.set(CookieType.AUTH_RESTART, "my-auth-restart");
@ -50,14 +51,40 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest {
cookies.set(CookieType.SESSION, "my-session", 444);
cookies.set(CookieType.WELCOME_CSRF, "my-welcome-csrf");
});
Assert.assertEquals(11, response.getCookies().size());
assertCookie(response, "AUTH_SESSION_ID", "my-auth-session-id", "/auth/realms/master/", -1, false, true, "None", true);
assertCookie(response, "KC_RESTART", "my-auth-restart", "/auth/realms/master/", -1, false, true, "None", false);
Assert.assertEquals(8, response.getCookies().size());
assertCookie(response, "AUTH_SESSION_ID", "my-auth-session-id", "/auth/realms/master/", -1, true, true, "None", true);
assertCookie(response, "KC_RESTART", "my-auth-restart", "/auth/realms/master/", -1, true, true, "None", false);
assertCookie(response, "KC_STATE_CHECKER", "my-auth-detached", "/auth/realms/master/", 222, true, true, "Strict", false);
assertCookie(response, "KEYCLOAK_IDENTITY", "my-identity", "/auth/realms/master/", 333, true, true, "None", true);
assertCookie(response, "KEYCLOAK_LOCALE", "my-locale", "/auth/realms/master/", -1, true, true, "None", false);
assertCookie(response, "KEYCLOAK_REMEMBER_ME", "my-username", "/auth/realms/master/", 31536000, true, true, "None", false);
assertCookie(response, "KEYCLOAK_SESSION", "my-session", "/auth/realms/master/", 444, true, false, "None", true);
assertCookie(response, "WELCOME_STATE_CHECKER", "my-welcome-csrf", "/auth/realms/master/testing/run-on-server", 300, true, true, "Strict", false);
}
@Test
public void testCookieDefaultsWithInsecureContext() {
KeycloakTestingClient testingInsecure = KeycloakTestingClient.getInstance("http://127.0.0.1.nip.io:8180/auth");
Response response = testingInsecure.server("master").runWithResponse(session -> {
CookieProvider cookies = session.getProvider(CookieProvider.class);
cookies.set(CookieType.AUTH_SESSION_ID, "my-auth-session-id");
cookies.set(CookieType.AUTH_RESTART, "my-auth-restart");
cookies.set(CookieType.AUTH_DETACHED, "my-auth-detached", 222);
cookies.set(CookieType.IDENTITY, "my-identity", 333);
cookies.set(CookieType.LOCALE, "my-locale");
cookies.set(CookieType.LOGIN_HINT, "my-username");
cookies.set(CookieType.SESSION, "my-session", 444);
cookies.set(CookieType.WELCOME_CSRF, "my-welcome-csrf");
});
Assert.assertEquals(8, response.getCookies().size());
assertCookie(response, "AUTH_SESSION_ID", "my-auth-session-id", "/auth/realms/master/", -1, false, true, "Lax", true);
assertCookie(response, "KC_RESTART", "my-auth-restart", "/auth/realms/master/", -1, false, true, "Lax", false);
assertCookie(response, "KC_STATE_CHECKER", "my-auth-detached", "/auth/realms/master/", 222, false, true, "Strict", false);
assertCookie(response, "KEYCLOAK_IDENTITY", "my-identity", "/auth/realms/master/", 333, false, true, "None", true);
assertCookie(response, "KEYCLOAK_LOCALE", "my-locale", "/auth/realms/master/", -1, false, true, "None", false);
assertCookie(response, "KEYCLOAK_REMEMBER_ME", "my-username", "/auth/realms/master/", 31536000, false, true, "None", false);
assertCookie(response, "KEYCLOAK_SESSION", "my-session", "/auth/realms/master/", 444, false, false, "None", true);
assertCookie(response, "KEYCLOAK_IDENTITY", "my-identity", "/auth/realms/master/", 333, false, true, "Lax", true);
assertCookie(response, "KEYCLOAK_LOCALE", "my-locale", "/auth/realms/master/", -1, false, true, "Lax", false);
assertCookie(response, "KEYCLOAK_REMEMBER_ME", "my-username", "/auth/realms/master/", 31536000, false, true, "Lax", false);
assertCookie(response, "KEYCLOAK_SESSION", "my-session", "/auth/realms/master/", 444, false, false, "Lax", true);
assertCookie(response, "WELCOME_STATE_CHECKER", "my-welcome-csrf", "/auth/realms/master/testing/run-on-server", 300, false, true, "Strict", false);
}
@ -112,34 +139,20 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest {
}
@Test
public void testSameSiteLegacyGet() {
filter.setHeader("Cookie", "AUTH_SESSION_ID=new;AUTH_SESSION_ID_LEGACY=legacy;KC_RESTART_LEGACY=ignore");
testing.server().run(session -> {
Assert.assertEquals("new", session.getProvider(CookieProvider.class).get(CookieType.AUTH_SESSION_ID));
Assert.assertEquals(null, session.getProvider(CookieProvider.class).get(CookieType.AUTH_RESTART));
});
filter.setHeader("Cookie", "AUTH_SESSION_ID_LEGACY=legacy");
testing.server().run(session -> {
Assert.assertEquals("legacy", session.getProvider(CookieProvider.class).get(CookieType.AUTH_SESSION_ID));
});
}
@Test
public void testSameSiteLegacyExpire() {
filter.setHeader("Cookie", "AUTH_SESSION_ID=new; AUTH_SESSION_ID_LEGACY=legacy; KC_RESTART_LEGACY=ignore; KEYCLOAK_LOCALE=foobar");
public void testExpireOldUnused() {
filter.setHeader("Cookie", "AUTH_SESSION_ID_LEGACY=legacy; KEYCLOAK_IDENTITY_LEGACY=legacy; KEYCLOAK_SESSION_LEGACY=ignore");
Response response = testing.server().runWithResponse(session -> {
session.getProvider(CookieProvider.class).expire(CookieType.AUTH_SESSION_ID);
session.getProvider(CookieProvider.class).expire(CookieType.AUTH_RESTART);
Assert.assertNull(session.getProvider(CookieProvider.class).get(CookieType.AUTH_SESSION_ID));
Assert.assertNull(session.getProvider(CookieProvider.class).get(CookieType.IDENTITY));
Assert.assertNull(session.getProvider(CookieProvider.class).get(CookieType.SESSION));
});
Map<String, NewCookie> cookies = response.getCookies();
Assert.assertEquals(2, cookies.size());
assertCookie(response, "AUTH_SESSION_ID", "", "/auth/realms/master/", 0, false, false, null, false);
Assert.assertEquals(3, cookies.size());
assertCookie(response, "AUTH_SESSION_ID_LEGACY", "", "/auth/realms/master/", 0, false, false, null, false);
assertCookie(response, "KEYCLOAK_IDENTITY_LEGACY", "", "/auth/realms/master/", 0, false, false, null, false);
assertCookie(response, "KEYCLOAK_SESSION_LEGACY", "", "/auth/realms/master/", 0, false, false, null, false);
}
@Test
@ -158,14 +171,14 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest {
assertCookie(response, "mycookie", "myvalue", "/auth/realms/master/testing/run-on-server", 1232, false, false, null, false);
}
private void assertCookie(Response response, String name, String value, String path, int maxAge, boolean secure, boolean httpOnly, String sameSite, boolean verifyLegacy) {
private void assertCookie(Response response, String name, String value, String path, int maxAge, boolean secure, boolean httpOnly, String sameSite, boolean verifyLegacyNotSent) {
Map<String, NewCookie> cookies = response.getCookies();
NewCookie cookie = cookies.get(name);
Assert.assertNotNull(cookie);
Assert.assertEquals(value, cookie.getValue());
Assert.assertEquals(path, cookie.getPath());
Assert.assertEquals(maxAge, cookie.getMaxAge());
Assert.assertEquals(secure || "None".equals(sameSite), cookie.isSecure());
Assert.assertEquals(secure, cookie.isSecure());
Assert.assertEquals(httpOnly, cookie.isHttpOnly());
String setHeader = getSetHeader(response, name);
@ -175,8 +188,8 @@ public class DefaultCookieProviderTest extends AbstractKeycloakTest {
Assert.assertTrue("Expected SameSite=" + sameSite + ", header was: " + setHeader, setHeader.contains("SameSite=" + sameSite));
}
if (verifyLegacy) {
assertCookie(response, name + "_LEGACY", value, path, maxAge, secure, httpOnly, null, false);
if (verifyLegacyNotSent) {
Assert.assertNull(response.getCookies().get(name + "_LEGACY"));
}
}

View file

@ -266,16 +266,11 @@ public class UserStorageTest extends AbstractAuthTest {
driver.navigate().to(oauth.AUTH_SERVER_ROOT + "/realms/" + testRealmResource().toRepresentation().getRealm() + "/login-actions/authenticate/" );
Cookie sameSiteSessionCookie = driver.manage().getCookieNamed(CookieType.SESSION.getName());
Cookie legacySessionCookie = driver.manage().getCookieNamed(CookieType.SESSION.getSameSiteLegacyName());
String cookieValue = sameSiteSessionCookie.getValue();
assertThat(cookieValue.contains("spécial"), is(false));
assertThat(cookieValue.contains("sp%C3%A9cial"), is(true));
String legacyCookieValue = legacySessionCookie.getValue();
assertThat(legacyCookieValue.contains("spécial"), is(false));
assertThat(legacyCookieValue.contains("sp%C3%A9cial"), is(true));
AccountHelper.logout(testRealmResource(), "spécial");
}