fix: use cookie-based storage for Supabase client auth

Store auth session in cookies instead of localStorage so server-side
code (admin layout, API routes, protected layout) can read the session.
Implements chunked cookie support for large session tokens.

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
This commit is contained in:
Claude
2026-02-10 17:09:14 +00:00
parent 7be84681f0
commit 1026a68427

View File

@@ -1,9 +1,74 @@
import { createClient as createSupabaseClient } from "@supabase/supabase-js";
import type { Database } from "@/types/database.types";
// Client Supabase côté navigateur (composants client)
// Storage basé sur les cookies pour que le serveur puisse lire la session
// Remplace le localStorage par défaut de Supabase
const cookieStorage = {
getItem: (key: string): string | null => {
if (typeof document === "undefined") return null;
// Essayer le cookie direct
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const match = document.cookie.match(
new RegExp(`(?:^|; )${escaped}=([^;]*)`)
);
if (match) return decodeURIComponent(match[1]);
// Essayer les cookies chunked (.0, .1, .2, ...)
const chunks: string[] = [];
for (let i = 0; i < 10; i++) {
const chunkMatch = document.cookie.match(
new RegExp(`(?:^|; )${escaped}\\.${i}=([^;]*)`)
);
if (chunkMatch) {
chunks.push(decodeURIComponent(chunkMatch[1]));
} else {
break;
}
}
if (chunks.length > 0) return chunks.join("");
return null;
},
setItem: (key: string, value: string): void => {
if (typeof document === "undefined") return;
// Supprimer les anciens cookies d'abord
cookieStorage.removeItem(key);
const maxChunkSize = 3500; // Limite cookie ~4KB avec overhead
if (value.length <= maxChunkSize) {
document.cookie = `${key}=${encodeURIComponent(value)}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=Lax`;
} else {
// Découper en chunks
for (let i = 0; i * maxChunkSize < value.length; i++) {
const chunk = value.substring(i * maxChunkSize, (i + 1) * maxChunkSize);
document.cookie = `${key}.${i}=${encodeURIComponent(chunk)}; path=/; max-age=${60 * 60 * 24 * 365}; SameSite=Lax`;
}
}
},
removeItem: (key: string): void => {
if (typeof document === "undefined") return;
document.cookie = `${key}=; path=/; max-age=0`;
for (let i = 0; i < 10; i++) {
document.cookie = `${key}.${i}=; path=/; max-age=0`;
}
},
};
// Client Supabase côté navigateur
// Utilise les cookies comme storage pour que le serveur puisse lire la session
export const createClient = () =>
createSupabaseClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: {
storage: cookieStorage,
flowType: "pkce",
},
}
);