From f0baf4f3484f57113b8943af7872280d2fd69754 Mon Sep 17 00:00:00 2001 From: Giancarlo Buomprisco Date: Sun, 11 Jan 2026 15:10:49 +0100 Subject: [PATCH] Fix cookie sameSite attribute (#443) * fix: set i18n cookie SameSite=lax to survive cross-domain redirects The i18n language cookie was being reset to default after Supabase email verification redirects because the cookie was using the default SameSite=strict setting which doesn't survive cross-domain navigation. Changes: - Set cookieOptions.sameSite to 'lax' to allow cookie to persist through cross-domain redirects (like Supabase auth flows) - Set secure flag dynamically based on HTTPS protocol - Set cookie path to '/' for site-wide availability - Set cookie expiration to 1 year - Change detection order to prioritize cookie over htmlTag Bumps version to 2.23.4 * fix: set theme cookie SameSite=lax to survive cross-domain redirects Apply the same fix as the i18n cookie to the theme cookie. The theme preference cookie was also missing SameSite=lax attribute, causing it to potentially be lost during cross-domain authentication flows. Changes: - Add SameSite=lax to theme cookie - Add Secure flag conditionally based on HTTPS protocol - Fix typo: setCookeTheme -> setCookieTheme * fix: correct SameSite casing and add security to sidebar cookie - Fix SameSite value to use proper capitalization (Lax instead of lax) for better browser compatibility - Add SameSite=Lax and conditional Secure flag to sidebar state cookie - Add typeof window check for defensive programming --------- Co-authored-by: Claude --- package.json | 2 +- packages/i18n/src/i18n.client.ts | 8 +++++++- packages/ui/src/makerkit/mobile-mode-toggle.tsx | 3 ++- packages/ui/src/makerkit/mode-toggle.tsx | 9 +++++---- packages/ui/src/shadcn/sidebar.tsx | 3 ++- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 6c6d88562..8623d2509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-supabase-saas-kit-turbo", - "version": "2.23.3", + "version": "2.23.4", "private": true, "sideEffects": false, "engines": { diff --git a/packages/i18n/src/i18n.client.ts b/packages/i18n/src/i18n.client.ts index 8242c05c2..4bf842dec 100644 --- a/packages/i18n/src/i18n.client.ts +++ b/packages/i18n/src/i18n.client.ts @@ -43,9 +43,15 @@ export async function initializeI18nClient( { ...settings, detection: { - order: ['htmlTag', 'cookie', 'navigator'], + order: ['cookie', 'htmlTag', 'navigator'], caches: ['cookie'], lookupCookie: 'lang', + cookieMinutes: 60 * 24 * 365, // 1 year + cookieOptions: { + sameSite: 'lax', + secure: typeof window !== 'undefined' && window.location.protocol === 'https:', + path: '/', + }, }, interpolation: { escapeValue: false, diff --git a/packages/ui/src/makerkit/mobile-mode-toggle.tsx b/packages/ui/src/makerkit/mobile-mode-toggle.tsx index 92c9f9ee8..6c370a62f 100644 --- a/packages/ui/src/makerkit/mobile-mode-toggle.tsx +++ b/packages/ui/src/makerkit/mobile-mode-toggle.tsx @@ -31,5 +31,6 @@ export function MobileModeToggle(props: { className?: string }) { } function setCookieTheme(theme: string) { - document.cookie = `theme=${theme}; path=/; max-age=31536000`; + const secure = typeof window !== 'undefined' && window.location.protocol === 'https:'; + document.cookie = `theme=${theme}; path=/; max-age=31536000; SameSite=Lax${secure ? '; Secure' : ''}`; } diff --git a/packages/ui/src/makerkit/mode-toggle.tsx b/packages/ui/src/makerkit/mode-toggle.tsx index 3cfc05175..69b9d6cd5 100644 --- a/packages/ui/src/makerkit/mode-toggle.tsx +++ b/packages/ui/src/makerkit/mode-toggle.tsx @@ -36,7 +36,7 @@ export function ModeToggle(props: { className?: string }) { key={mode} onClick={() => { setTheme(mode); - setCookeTheme(mode); + setCookieTheme(mode); }} > @@ -80,7 +80,7 @@ export function SubMenuModeToggle() { key={mode} onClick={() => { setTheme(mode); - setCookeTheme(mode); + setCookieTheme(mode); }} > @@ -125,8 +125,9 @@ export function SubMenuModeToggle() { ); } -function setCookeTheme(theme: string) { - document.cookie = `theme=${theme}; path=/; max-age=31536000`; +function setCookieTheme(theme: string) { + const secure = typeof window !== 'undefined' && window.location.protocol === 'https:'; + document.cookie = `theme=${theme}; path=/; max-age=31536000; SameSite=Lax${secure ? '; Secure' : ''}`; } function Icon({ theme }: { theme: string | undefined }) { diff --git a/packages/ui/src/shadcn/sidebar.tsx b/packages/ui/src/shadcn/sidebar.tsx index d139181ac..560bd3bfd 100644 --- a/packages/ui/src/shadcn/sidebar.tsx +++ b/packages/ui/src/shadcn/sidebar.tsx @@ -96,7 +96,8 @@ const SidebarProvider: React.FC< _setOpen(value); // This sets the cookie to keep the sidebar state. - document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + const secure = typeof window !== 'undefined' && window.location.protocol === 'https:'; + document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}; SameSite=Lax${secure ? '; Secure' : ''}`; }, [setOpenProp, open], );