From 77e9f6f2e90505236a2dbdd9125f6ba8aa06ce6e Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 30 Apr 2025 09:41:00 +0200 Subject: [PATCH] override cookie sameSite settings --- apps/login/src/lib/cookies.ts | 60 ++++++++++++++------- apps/login/src/lib/server/cookie.ts | 79 ++++++++++++++++++---------- apps/login/src/lib/server/session.ts | 11 +++- 3 files changed, 99 insertions(+), 51 deletions(-) diff --git a/apps/login/src/lib/cookies.ts b/apps/login/src/lib/cookies.ts index cf762b904f..76f5580a16 100644 --- a/apps/login/src/lib/cookies.ts +++ b/apps/login/src/lib/cookies.ts @@ -20,7 +20,10 @@ export type Cookie = { type SessionCookie = Cookie & T; -async function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { +async function setSessionHttpOnlyCookie( + sessions: SessionCookie[], + sameSite: boolean | "lax" | "strict" | "none" = true, +) { const cookiesList = await cookies(); return cookiesList.set({ @@ -28,6 +31,7 @@ async function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { value: JSON.stringify(sessions), httpOnly: true, path: "/", + sameSite, }); } @@ -42,10 +46,15 @@ export async function setLanguageCookie(language: string) { }); } -export async function addSessionToCookie( - session: SessionCookie, - cleanup: boolean = false, -): Promise { +export async function addSessionToCookie({ + session, + cleanup, + sameSite, +}: { + session: SessionCookie; + cleanup?: boolean; + sameSite?: boolean | "lax" | "strict" | "none" | undefined; +}): Promise { const cookiesList = await cookies(); const stringifiedCookie = cookiesList.get("sessions"); @@ -79,17 +88,23 @@ export async function addSessionToCookie( ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now : true, ); - return setSessionHttpOnlyCookie(filteredSessions); + return setSessionHttpOnlyCookie(filteredSessions, sameSite); } else { - return setSessionHttpOnlyCookie(currentSessions); + return setSessionHttpOnlyCookie(currentSessions, sameSite); } } -export async function updateSessionCookie( - id: string, - session: SessionCookie, - cleanup: boolean = false, -): Promise { +export async function updateSessionCookie({ + id, + session, + cleanup, + sameSite, +}: { + id: string; + session: SessionCookie; + cleanup?: boolean; + sameSite?: boolean | "lax" | "strict" | "none" | undefined; +}): Promise { const cookiesList = await cookies(); const stringifiedCookie = cookiesList.get("sessions"); @@ -108,19 +123,24 @@ export async function updateSessionCookie( ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now : true, ); - return setSessionHttpOnlyCookie(filteredSessions); + return setSessionHttpOnlyCookie(filteredSessions, sameSite); } else { - return setSessionHttpOnlyCookie(sessions); + return setSessionHttpOnlyCookie(sessions, sameSite); } } else { throw "updateSessionCookie: session id now found"; } } -export async function removeSessionFromCookie( - session: SessionCookie, - cleanup: boolean = false, -): Promise { +export async function removeSessionFromCookie({ + session, + cleanup, + sameSite, +}: { + session: SessionCookie; + cleanup?: boolean; + sameSite?: boolean | "lax" | "strict" | "none" | undefined; +}): Promise { const cookiesList = await cookies(); const stringifiedCookie = cookiesList.get("sessions"); @@ -136,9 +156,9 @@ export async function removeSessionFromCookie( ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now : true, ); - return setSessionHttpOnlyCookie(filteredSessions); + return setSessionHttpOnlyCookie(filteredSessions, sameSite); } else { - return setSessionHttpOnlyCookie(reducedSessions); + return setSessionHttpOnlyCookie(reducedSessions, sameSite); } } diff --git a/apps/login/src/lib/server/cookie.ts b/apps/login/src/lib/server/cookie.ts index d54a4047b1..7cc86e9337 100644 --- a/apps/login/src/lib/server/cookie.ts +++ b/apps/login/src/lib/server/cookie.ts @@ -4,6 +4,7 @@ import { addSessionToCookie, updateSessionCookie } from "@/lib/cookies"; import { createSessionForUserIdAndIdpIntent, createSessionFromChecks, + getSecuritySettings, getSession, setSession, } from "@/lib/zitadel"; @@ -65,7 +66,7 @@ export async function createSessionAndUpdateCookie(command: { serviceUrl, sessionId: createdSession.sessionId, sessionToken: createdSession.sessionToken, - }).then((response) => { + }).then(async (response) => { if (response?.session && response.session?.factors?.user?.loginName) { const sessionCookie: CustomCookieData = { id: createdSession.sessionId, @@ -91,9 +92,14 @@ export async function createSessionAndUpdateCookie(command: { response.session.factors.user.organizationId; } - return addSessionToCookie(sessionCookie).then(() => { - return response.session as Session; - }); + const securitySettings = await getSecuritySettings({ serviceUrl }); + const sameSite = securitySettings?.embeddedIframe?.enabled + ? "none" + : true; + + await addSessionToCookie({ session: sessionCookie, sameSite }); + + return response.session as Session; } else { throw "could not get session or session does not have loginName"; } @@ -167,7 +173,10 @@ export async function createSessionForIdpAndUpdateCookie( sessionCookie.organization = session.factors.user.organizationId; } - return addSessionToCookie(sessionCookie).then(() => { + const securitySettings = await getSecuritySettings({ serviceUrl }); + const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true; + + return addSessionToCookie({ session: sessionCookie, sameSite }).then(() => { return session as Session; }); } @@ -217,32 +226,44 @@ export async function setSessionAndUpdateCookie( serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, - }).then((response) => { - if (response?.session && response.session.factors?.user?.loginName) { - const { session } = response; - const newCookie: CustomCookieData = { - id: sessionCookie.id, - token: updatedSession.sessionToken, - creationTs: sessionCookie.creationTs, - expirationTs: sessionCookie.expirationTs, - // just overwrite the changeDate with the new one - changeTs: updatedSession.details?.changeDate - ? `${timestampMs(updatedSession.details.changeDate)}` - : "", - loginName: session.factors?.user?.loginName ?? "", - organization: session.factors?.user?.organizationId ?? "", - }; - - if (sessionCookie.requestId) { - newCookie.requestId = sessionCookie.requestId; - } - - return updateSessionCookie(sessionCookie.id, newCookie).then(() => { - return { challenges: updatedSession.challenges, ...session }; - }); - } else { + }).then(async (response) => { + if ( + !response?.session || + !response.session.factors?.user?.loginName + ) { throw "could not get session or session does not have loginName"; } + + const { session } = response; + const newCookie: CustomCookieData = { + id: sessionCookie.id, + token: updatedSession.sessionToken, + creationTs: sessionCookie.creationTs, + expirationTs: sessionCookie.expirationTs, + // just overwrite the changeDate with the new one + changeTs: updatedSession.details?.changeDate + ? `${timestampMs(updatedSession.details.changeDate)}` + : "", + loginName: session.factors?.user?.loginName ?? "", + organization: session.factors?.user?.organizationId ?? "", + }; + + if (sessionCookie.requestId) { + newCookie.requestId = sessionCookie.requestId; + } + + const securitySettings = await getSecuritySettings({ serviceUrl }); + const sameSite = securitySettings?.embeddedIframe?.enabled + ? "none" + : true; + + return updateSessionCookie({ + id: sessionCookie.id, + session: newCookie, + sameSite, + }).then(() => { + return { challenges: updatedSession.challenges, ...session }; + }); }); } else { throw "Session not be set"; diff --git a/apps/login/src/lib/server/session.ts b/apps/login/src/lib/server/session.ts index 66688bf415..3ff3d14017 100644 --- a/apps/login/src/lib/server/session.ts +++ b/apps/login/src/lib/server/session.ts @@ -4,6 +4,7 @@ import { setSessionAndUpdateCookie } from "@/lib/server/cookie"; import { deleteSession, getLoginSettings, + getSecuritySettings, humanMFAInitSkipped, listAuthenticationMethodTypes, } from "@/lib/zitadel"; @@ -209,8 +210,11 @@ export async function clearSession(options: ClearSessionOptions) { sessionToken: session.token, }); + const securitySettings = await getSecuritySettings({ serviceUrl }); + const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true; + if (deletedSession) { - return removeSessionFromCookie(session); + return removeSessionFromCookie({ session, sameSite }); } } @@ -230,9 +234,12 @@ export async function cleanupSession({ sessionId }: CleanupSessionCommand) { sessionToken: sessionCookie.token, }); + const securitySettings = await getSecuritySettings({ serviceUrl }); + const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true; + if (!deleteResponse) { throw new Error("Could not delete session"); } - return removeSessionFromCookie(sessionCookie); + return removeSessionFromCookie({ session: sessionCookie, sameSite }); }