override cookie sameSite settings

This commit is contained in:
Max Peintner
2025-04-30 09:41:00 +02:00
parent a690b254c4
commit 77e9f6f2e9
3 changed files with 99 additions and 51 deletions

View File

@@ -20,7 +20,10 @@ export type Cookie = {
type SessionCookie<T> = Cookie & T; type SessionCookie<T> = Cookie & T;
async function setSessionHttpOnlyCookie<T>(sessions: SessionCookie<T>[]) { async function setSessionHttpOnlyCookie<T>(
sessions: SessionCookie<T>[],
sameSite: boolean | "lax" | "strict" | "none" = true,
) {
const cookiesList = await cookies(); const cookiesList = await cookies();
return cookiesList.set({ return cookiesList.set({
@@ -28,6 +31,7 @@ async function setSessionHttpOnlyCookie<T>(sessions: SessionCookie<T>[]) {
value: JSON.stringify(sessions), value: JSON.stringify(sessions),
httpOnly: true, httpOnly: true,
path: "/", path: "/",
sameSite,
}); });
} }
@@ -42,10 +46,15 @@ export async function setLanguageCookie(language: string) {
}); });
} }
export async function addSessionToCookie<T>( export async function addSessionToCookie<T>({
session: SessionCookie<T>, session,
cleanup: boolean = false, cleanup,
): Promise<any> { sameSite,
}: {
session: SessionCookie<T>;
cleanup?: boolean;
sameSite?: boolean | "lax" | "strict" | "none" | undefined;
}): Promise<any> {
const cookiesList = await cookies(); const cookiesList = await cookies();
const stringifiedCookie = cookiesList.get("sessions"); const stringifiedCookie = cookiesList.get("sessions");
@@ -79,17 +88,23 @@ export async function addSessionToCookie<T>(
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
: true, : true,
); );
return setSessionHttpOnlyCookie(filteredSessions); return setSessionHttpOnlyCookie(filteredSessions, sameSite);
} else { } else {
return setSessionHttpOnlyCookie(currentSessions); return setSessionHttpOnlyCookie(currentSessions, sameSite);
} }
} }
export async function updateSessionCookie<T>( export async function updateSessionCookie<T>({
id: string, id,
session: SessionCookie<T>, session,
cleanup: boolean = false, cleanup,
): Promise<any> { sameSite,
}: {
id: string;
session: SessionCookie<T>;
cleanup?: boolean;
sameSite?: boolean | "lax" | "strict" | "none" | undefined;
}): Promise<any> {
const cookiesList = await cookies(); const cookiesList = await cookies();
const stringifiedCookie = cookiesList.get("sessions"); const stringifiedCookie = cookiesList.get("sessions");
@@ -108,19 +123,24 @@ export async function updateSessionCookie<T>(
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
: true, : true,
); );
return setSessionHttpOnlyCookie(filteredSessions); return setSessionHttpOnlyCookie(filteredSessions, sameSite);
} else { } else {
return setSessionHttpOnlyCookie(sessions); return setSessionHttpOnlyCookie(sessions, sameSite);
} }
} else { } else {
throw "updateSessionCookie<T>: session id now found"; throw "updateSessionCookie<T>: session id now found";
} }
} }
export async function removeSessionFromCookie<T>( export async function removeSessionFromCookie<T>({
session: SessionCookie<T>, session,
cleanup: boolean = false, cleanup,
): Promise<any> { sameSite,
}: {
session: SessionCookie<T>;
cleanup?: boolean;
sameSite?: boolean | "lax" | "strict" | "none" | undefined;
}): Promise<any> {
const cookiesList = await cookies(); const cookiesList = await cookies();
const stringifiedCookie = cookiesList.get("sessions"); const stringifiedCookie = cookiesList.get("sessions");
@@ -136,9 +156,9 @@ export async function removeSessionFromCookie<T>(
? timestampDate(timestampFromMs(Number(session.expirationTs))) > now ? timestampDate(timestampFromMs(Number(session.expirationTs))) > now
: true, : true,
); );
return setSessionHttpOnlyCookie(filteredSessions); return setSessionHttpOnlyCookie(filteredSessions, sameSite);
} else { } else {
return setSessionHttpOnlyCookie(reducedSessions); return setSessionHttpOnlyCookie(reducedSessions, sameSite);
} }
} }

View File

@@ -4,6 +4,7 @@ import { addSessionToCookie, updateSessionCookie } from "@/lib/cookies";
import { import {
createSessionForUserIdAndIdpIntent, createSessionForUserIdAndIdpIntent,
createSessionFromChecks, createSessionFromChecks,
getSecuritySettings,
getSession, getSession,
setSession, setSession,
} from "@/lib/zitadel"; } from "@/lib/zitadel";
@@ -65,7 +66,7 @@ export async function createSessionAndUpdateCookie(command: {
serviceUrl, serviceUrl,
sessionId: createdSession.sessionId, sessionId: createdSession.sessionId,
sessionToken: createdSession.sessionToken, sessionToken: createdSession.sessionToken,
}).then((response) => { }).then(async (response) => {
if (response?.session && response.session?.factors?.user?.loginName) { if (response?.session && response.session?.factors?.user?.loginName) {
const sessionCookie: CustomCookieData = { const sessionCookie: CustomCookieData = {
id: createdSession.sessionId, id: createdSession.sessionId,
@@ -91,9 +92,14 @@ export async function createSessionAndUpdateCookie(command: {
response.session.factors.user.organizationId; response.session.factors.user.organizationId;
} }
return addSessionToCookie(sessionCookie).then(() => { const securitySettings = await getSecuritySettings({ serviceUrl });
return response.session as Session; const sameSite = securitySettings?.embeddedIframe?.enabled
}); ? "none"
: true;
await addSessionToCookie({ session: sessionCookie, sameSite });
return response.session as Session;
} else { } else {
throw "could not get session or session does not have loginName"; 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; 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; return session as Session;
}); });
} }
@@ -217,32 +226,44 @@ export async function setSessionAndUpdateCookie(
serviceUrl, serviceUrl,
sessionId: sessionCookie.id, sessionId: sessionCookie.id,
sessionToken: sessionCookie.token, sessionToken: sessionCookie.token,
}).then((response) => { }).then(async (response) => {
if (response?.session && response.session.factors?.user?.loginName) { if (
const { session } = response; !response?.session ||
const newCookie: CustomCookieData = { !response.session.factors?.user?.loginName
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 {
throw "could not get session or session does not have 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 { } else {
throw "Session not be set"; throw "Session not be set";

View File

@@ -4,6 +4,7 @@ import { setSessionAndUpdateCookie } from "@/lib/server/cookie";
import { import {
deleteSession, deleteSession,
getLoginSettings, getLoginSettings,
getSecuritySettings,
humanMFAInitSkipped, humanMFAInitSkipped,
listAuthenticationMethodTypes, listAuthenticationMethodTypes,
} from "@/lib/zitadel"; } from "@/lib/zitadel";
@@ -209,8 +210,11 @@ export async function clearSession(options: ClearSessionOptions) {
sessionToken: session.token, sessionToken: session.token,
}); });
const securitySettings = await getSecuritySettings({ serviceUrl });
const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true;
if (deletedSession) { if (deletedSession) {
return removeSessionFromCookie(session); return removeSessionFromCookie({ session, sameSite });
} }
} }
@@ -230,9 +234,12 @@ export async function cleanupSession({ sessionId }: CleanupSessionCommand) {
sessionToken: sessionCookie.token, sessionToken: sessionCookie.token,
}); });
const securitySettings = await getSecuritySettings({ serviceUrl });
const sameSite = securitySettings?.embeddedIframe?.enabled ? "none" : true;
if (!deleteResponse) { if (!deleteResponse) {
throw new Error("Could not delete session"); throw new Error("Could not delete session");
} }
return removeSessionFromCookie(sessionCookie); return removeSessionFromCookie({ session: sessionCookie, sameSite });
} }