From 41f9ddd70c857c39937dc850a72f1379882a03ac Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Mon, 30 Mar 2026 16:08:29 +0300 Subject: [PATCH] docs: update setAll callbacks to accept cache headers second argument (#44240) ## What Updates all `setAll` cookie handler implementations across docs and examples to accept the new `headers` second argument introduced in `@supabase/ssr` v0.10.0 ([supabase/ssr#176](https://github.com/supabase/ssr/pull/176)). ## Why `@supabase/ssr` v0.10.0 introduced a breaking change: `setAll` now receives a required second argument `headers: Record` alongside the cookies array. When a token refresh occurs, the library passes cache headers (`Cache-Control`, `Expires`, `Pragma`) that must be applied to the HTTP response to prevent CDN caching of auth responses. Because TypeScript allows functions with fewer parameters to satisfy a type expecting more, existing `setAll` implementations do not produce a type error when the second argument is omitted. Users who copy an outdated snippet will silently miss the CDN protection. Root cause and context: [supabase/supabase-js#1682](https://github.com/supabase/supabase-js/issues/1682) ## Changes **Proxy/middleware contexts** (where token refreshes happen) now apply the cache headers to their response: - Next.js proxy files: `supabaseResponse.headers.set(key, value)` - SvelteKit hooks: `event.setHeaders(headers)` - Hono middleware: `c.header(key, value)` - Pages Router (Express-style): `ctx.res.setHeader(key, value)` - Remix/React Router loaders and actions: applied to response headers (outer `headers` variable renamed to `responseHeaders` to avoid naming conflict with the new param) **Server Component and API route contexts** (no response object available) accept `_headers` without applying them. ## Files updated - `apps/docs/content/guides/auth/server-side/creating-a-client.mdx` (inline Astro, Remix, React Router, Express snippets) - `apps/docs/content/_partials/oauth_pkce_flow.mdx` - `apps/docs/content/guides/auth/oauth-server/getting-started.mdx` - `apps/docs/content/guides/auth/passwords.mdx` - `apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx` - `examples/auth/nextjs/`, `examples/auth/nextjs-full/` (proxy + server) - `examples/auth/sveltekit/`, `examples/auth/sveltekit-full/` - `examples/auth/hono/`, `examples/auth/hono-full/` - `examples/user-management/nextjs-user-management/` (proxy + server) - `examples/user-management/sveltekit-user-management/` - `examples/realtime/nextjs-authorization-demo/` (proxy + server) - `examples/realtime/nextjs-auth-presence/` (pages router) - `examples/prompts/nextjs-supabase-auth.md` --- .../content/_partials/oauth_pkce_flow.mdx | 22 ++++---- .../auth/oauth-server/getting-started.mdx | 4 +- apps/docs/content/guides/auth/passwords.mdx | 4 +- .../auth/server-side/advanced-guide.mdx | 4 +- .../auth/server-side/creating-a-client.mdx | 50 +++++++++++-------- ...ase-auth-helpers-to-ssr-package-5NRunM.mdx | 7 ++- .../src/middleware/auth.middleware.ts | 3 +- .../hono/src/middleware/auth.middleware.ts | 3 +- .../auth/nextjs-full/lib/supabase/proxy.ts | 5 +- .../auth/nextjs-full/lib/supabase/server.ts | 2 +- examples/auth/nextjs/lib/supabase/proxy.ts | 5 +- examples/auth/nextjs/lib/supabase/server.ts | 2 +- .../auth/sveltekit-full/src/hooks.server.ts | 5 +- examples/auth/sveltekit/src/hooks.server.ts | 5 +- examples/prompts/nextjs-supabase-auth.md | 14 ++++-- .../nextjs-auth-presence/pages/index.tsx | 3 +- .../nextjs-auth-presence/pages/profile.tsx | 3 +- .../utils/supabase/proxy.ts | 5 +- .../utils/supabase/server.ts | 2 +- .../lib/supabase/proxy.ts | 7 ++- .../lib/supabase/server.ts | 2 +- .../src/hooks.server.ts | 5 +- 22 files changed, 105 insertions(+), 57 deletions(-) diff --git a/apps/docs/content/_partials/oauth_pkce_flow.mdx b/apps/docs/content/_partials/oauth_pkce_flow.mdx index cb538ad0437..702df957356 100644 --- a/apps/docs/content/_partials/oauth_pkce_flow.mdx +++ b/apps/docs/content/_partials/oauth_pkce_flow.mdx @@ -156,7 +156,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { getAll() { return parseCookieHeader(Astro.request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { cookiesToSet.forEach(({ name, value, options }) => Astro.cookies.set(name, value, options) ) @@ -191,7 +191,7 @@ export async function loader({ request }: LoaderFunctionArgs) { const requestUrl = new URL(request.url) const code = requestUrl.searchParams.get('code') const next = requestUrl.searchParams.get('next') || '/' - const headers = new Headers() + const responseHeaders = new Headers() if (code) { const supabase = createServerClient( @@ -202,10 +202,11 @@ export async function loader({ request }: LoaderFunctionArgs) { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value, options }) => - headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) + responseHeaders.append('Set-Cookie', serializeCookieHeader(name, value, options)) ) + Object.entries(cacheHeaders).forEach(([key, value]) => responseHeaders.set(key, value)) }, }, } @@ -214,12 +215,12 @@ export async function loader({ request }: LoaderFunctionArgs) { const { error } = await supabase.auth.exchangeCodeForSession(code) if (!error) { - return redirect(next, { headers }) + return redirect(next, { headers: responseHeaders }) } } // return the user to an error page with instructions - return redirect('/auth/auth-code-error', { headers }) + return redirect('/auth/auth-code-error', { headers: responseHeaders }) } ``` @@ -240,11 +241,14 @@ app.get("/auth/callback", async function (req, res) { process.env.SUPABASE_PUBLISHABLE_KEY, { cookies: { getAll() { - return parseCookieHeader(context.req.headers.cookie ?? '') + return parseCookieHeader(req.headers.cookie ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value, options }) => - context.res.appendHeader('Set-Cookie', serializeCookieHeader(name, value, options)) + res.appendHeader('Set-Cookie', serializeCookieHeader(name, value, options)) + ) + Object.entries(headers).forEach(([key, value]) => + res.setHeader(key, value) ) }, }, diff --git a/apps/docs/content/guides/auth/oauth-server/getting-started.mdx b/apps/docs/content/guides/auth/oauth-server/getting-started.mdx index 0efdc4068e2..a0e2bdd40f6 100644 --- a/apps/docs/content/guides/auth/oauth-server/getting-started.mdx +++ b/apps/docs/content/guides/auth/oauth-server/getting-started.mdx @@ -212,7 +212,7 @@ export default async function ConsentPage({ { cookies: { getAll: async () => (await cookies()).getAll(), - setAll: async (cookiesToSet) => { + setAll: async (cookiesToSet, _headers) => { const cookieStore = await cookies() cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) }, @@ -297,7 +297,7 @@ export async function POST(request: Request) { { cookies: { getAll: async () => (await cookies()).getAll(), - setAll: async (cookiesToSet) => { + setAll: async (cookiesToSet, _headers) => { const cookieStore = await cookies() cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) }, diff --git a/apps/docs/content/guides/auth/passwords.mdx b/apps/docs/content/guides/auth/passwords.mdx index cfde4e71b7c..8675ed00a4e 100644 --- a/apps/docs/content/guides/auth/passwords.mdx +++ b/apps/docs/content/guides/auth/passwords.mdx @@ -280,7 +280,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { cookiesToSet.forEach(({ name, value, options }) => cookies.set(name, value, options)) }, }, @@ -801,7 +801,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { cookiesToSet.forEach(({ name, value, options }) => cookies.set(name, value, options)) }, }, diff --git a/apps/docs/content/guides/auth/server-side/advanced-guide.mdx b/apps/docs/content/guides/auth/server-side/advanced-guide.mdx index 23b06f8fdda..9b31620964e 100644 --- a/apps/docs/content/guides/auth/server-side/advanced-guide.mdx +++ b/apps/docs/content/guides/auth/server-side/advanced-guide.mdx @@ -72,7 +72,9 @@ Do not enable ISR on any route where authentication is handled or where a sessio When `@supabase/ssr` refreshes a session token server-side, it writes the updated JWT to the HTTP response via a `Set-Cookie` header. If your CDN (e.g. Vercel Edge, Cloudflare) caches that response and serves it to a different user, that user's browser will store the cached token and be signed in as the wrong person. -To prevent this, set `Cache-Control: private, no-store` on responses from any route that handles authentication, typically your middleware. Most CDNs respect this header and will not cache the response. +As of `@supabase/ssr` v0.10.0, the library automatically passes the necessary cache headers (`Cache-Control`, `Expires`, `Pragma`) to your `setAll` callback as a second argument whenever a token refresh occurs. If your `setAll` implementation applies those headers to the response (as shown in the examples in [Creating a Supabase client for SSR](/docs/guides/auth/server-side/creating-a-client)), no additional manual configuration is needed for most CDNs. + +If you are on an older version or need to set headers manually, add `Cache-Control: private, no-store` to responses from any route that handles authentication: #### Next.js middleware diff --git a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx index c702ae8b55c..068c1cbf750 100644 --- a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx +++ b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx @@ -183,7 +183,7 @@ The Proxy is responsible for: The cookies object lets the Supabase client know how to access the cookies, so it can read and write the user session data. To make `@supabase/ssr` framework-agnostic, the cookies methods aren't hard-coded. These utility functions adapt `@supabase/ssr`'s cookie handling for Next.js. - The `set` and `remove` methods for the server client need error handlers, because Next.js throws an error if cookies are set from Server Components. You can safely ignore this error because you'll set up Proxy in the next step to write refreshed cookies to storage. + `setAll` is called whenever the library needs to write cookies, for example after a token refresh. It receives two arguments: the array of cookies to set, and a `headers` object containing cache headers (`Cache-Control`, `Expires`, `Pragma`) that must be applied to the HTTP response to prevent CDNs from caching the response and leaking the session to other users. In the Proxy, apply these headers to the response. In Server Components, the headers cannot be set, which is why the `setAll` call is wrapped in a try/catch and the error is ignored. The Proxy handles writing cookies and headers on every request. The cookie is named `sb--auth-token` by default. @@ -346,9 +346,12 @@ const supabase = createServerClient( getAll() { return parseCookieHeader(Astro.request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => Astro.cookies.set(name, value)) + Object.entries(headers).forEach(([key, value]) => + Astro.response.headers.set(key, value) + ) }, }, } @@ -388,7 +391,7 @@ export async function GET(context: APIContext) { getAll() { return parseCookieHeader(context.request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { cookiesToSet.forEach(({ name, value }) => context.cookies.set(name, value)) }, @@ -417,7 +420,7 @@ export const onRequest = defineMiddleware(async (context, next) => { getAll() { return parseCookieHeader(context.request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { cookiesToSet.forEach(({ name, value }) => context.cookies.set(name, value)) }, }, @@ -452,7 +455,7 @@ import { type LoaderFunctionArgs } from '@remix-run/node' import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' export async function loader({ request }: LoaderFunctionArgs) { - const headers = new Headers() + const responseHeaders = new Headers() const supabase = createServerClient( process.env.SUPABASE_URL!, @@ -462,17 +465,18 @@ export async function loader({ request }: LoaderFunctionArgs) { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value, options }) => - headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) + responseHeaders.append('Set-Cookie', serializeCookieHeader(name, value, options)) ) + Object.entries(cacheHeaders).forEach(([key, value]) => responseHeaders.set(key, value)) }, }, } ) return new Response('...', { - headers, + headers: responseHeaders, }) } ``` @@ -486,7 +490,7 @@ import { type ActionFunctionArgs } from '@remix-run/node' import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' export async function action({ request }: ActionFunctionArgs) { - const headers = new Headers() + const responseHeaders = new Headers() const supabase = createServerClient( process.env.SUPABASE_URL!, @@ -496,17 +500,18 @@ export async function action({ request }: ActionFunctionArgs) { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value, options }) => - headers.append('Set-Cookie', serializeCookieHeader(name, value, options)) + responseHeaders.append('Set-Cookie', serializeCookieHeader(name, value, options)) ) + Object.entries(cacheHeaders).forEach(([key, value]) => responseHeaders.set(key, value)) }, }, } ) return new Response('...', { - headers, + headers: responseHeaders, }) } ``` @@ -563,23 +568,24 @@ import { LoaderFunctionArgs } from 'react-router' import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' export async function loader({ request }: LoaderFunctionArgs) { - const headers = new Headers() + const responseHeaders = new Headers() const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value }) => - headers.append('Set-Cookie', serializeCookieHeader(name, value)) + responseHeaders.append('Set-Cookie', serializeCookieHeader(name, value)) ) + Object.entries(cacheHeaders).forEach(([key, value]) => responseHeaders.set(key, value)) }, }, }) return new Response('...', { - headers, + headers: responseHeaders, }) } ``` @@ -593,23 +599,24 @@ import { type ActionFunctionArgs } from '@react-router' import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr' export async function action({ request }: ActionFunctionArgs) { - const headers = new Headers() + const responseHeaders = new Headers() const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { return parseCookieHeader(request.headers.get('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value }) => - headers.append('Set-Cookie', serializeCookieHeader(name, value)) + responseHeaders.append('Set-Cookie', serializeCookieHeader(name, value)) ) + Object.entries(cacheHeaders).forEach(([key, value]) => responseHeaders.set(key, value)) }, }, }) return new Response('...', { - headers, + headers: responseHeaders, }) } ``` @@ -670,10 +677,11 @@ exports.createClient = (context) => { getAll() { return parseCookieHeader(context.req.headers.cookie ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => context.res.appendHeader('Set-Cookie', serializeCookieHeader(name, value)) ) + Object.entries(headers).forEach(([key, value]) => context.res.setHeader(key, value)) }, }, }) diff --git a/apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx b/apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx index 427579b6489..954e619a0bf 100644 --- a/apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx +++ b/apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx @@ -51,7 +51,7 @@ export async function createClient() { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) } catch { @@ -84,12 +84,15 @@ export async function updateSession(request: NextRequest) { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, }) cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options)) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/auth/hono-full/src/middleware/auth.middleware.ts b/examples/auth/hono-full/src/middleware/auth.middleware.ts index fff32dd1bca..89891943a57 100644 --- a/examples/auth/hono-full/src/middleware/auth.middleware.ts +++ b/examples/auth/hono-full/src/middleware/auth.middleware.ts @@ -39,8 +39,9 @@ export const supabaseMiddleware = (): MiddlewareHandler => { getAll() { return parseCookieHeader(c.req.header('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value, options }) => setCookie(c, name, value, options)) + Object.entries(cacheHeaders).forEach(([key, value]) => c.header(key, value)) }, }, }) diff --git a/examples/auth/hono/src/middleware/auth.middleware.ts b/examples/auth/hono/src/middleware/auth.middleware.ts index 69c74ba8c1e..b45a0958778 100644 --- a/examples/auth/hono/src/middleware/auth.middleware.ts +++ b/examples/auth/hono/src/middleware/auth.middleware.ts @@ -38,8 +38,9 @@ export const supabaseMiddleware = (): MiddlewareHandler => { getAll() { return parseCookieHeader(c.req.header('Cookie') ?? '') }, - setAll(cookiesToSet) { + setAll(cookiesToSet, cacheHeaders) { cookiesToSet.forEach(({ name, value, options }) => setCookie(c, name, value, options)) + Object.entries(cacheHeaders).forEach(([key, value]) => c.header(key, value)) }, }, }) diff --git a/examples/auth/nextjs-full/lib/supabase/proxy.ts b/examples/auth/nextjs-full/lib/supabase/proxy.ts index ee8dac4269f..ddc4e84502e 100644 --- a/examples/auth/nextjs-full/lib/supabase/proxy.ts +++ b/examples/auth/nextjs-full/lib/supabase/proxy.ts @@ -16,7 +16,7 @@ export async function updateSession(request: NextRequest) { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, @@ -24,6 +24,9 @@ export async function updateSession(request: NextRequest) { cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/auth/nextjs-full/lib/supabase/server.ts b/examples/auth/nextjs-full/lib/supabase/server.ts index ea9271818ab..60904a436f9 100644 --- a/examples/auth/nextjs-full/lib/supabase/server.ts +++ b/examples/auth/nextjs-full/lib/supabase/server.ts @@ -12,7 +12,7 @@ export async function createClient() { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) diff --git a/examples/auth/nextjs/lib/supabase/proxy.ts b/examples/auth/nextjs/lib/supabase/proxy.ts index ee8dac4269f..ddc4e84502e 100644 --- a/examples/auth/nextjs/lib/supabase/proxy.ts +++ b/examples/auth/nextjs/lib/supabase/proxy.ts @@ -16,7 +16,7 @@ export async function updateSession(request: NextRequest) { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, @@ -24,6 +24,9 @@ export async function updateSession(request: NextRequest) { cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/auth/nextjs/lib/supabase/server.ts b/examples/auth/nextjs/lib/supabase/server.ts index aed1ce1830e..867ecbc085d 100644 --- a/examples/auth/nextjs/lib/supabase/server.ts +++ b/examples/auth/nextjs/lib/supabase/server.ts @@ -12,7 +12,7 @@ export async function createClient() { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) diff --git a/examples/auth/sveltekit-full/src/hooks.server.ts b/examples/auth/sveltekit-full/src/hooks.server.ts index 882d434770d..153400a63e0 100644 --- a/examples/auth/sveltekit-full/src/hooks.server.ts +++ b/examples/auth/sveltekit-full/src/hooks.server.ts @@ -18,10 +18,13 @@ const supabase: Handle = async ({ event, resolve }) => { * the cookie options. Setting `path` to `/` replicates previous/ * standard behavior. */ - setAll: (cookiesToSet) => { + setAll: (cookiesToSet, headers) => { cookiesToSet.forEach(({ name, value, options }) => { event.cookies.set(name, value, { ...options, path: '/' }) }) + if (Object.keys(headers).length > 0) { + event.setHeaders(headers) + } }, }, }) diff --git a/examples/auth/sveltekit/src/hooks.server.ts b/examples/auth/sveltekit/src/hooks.server.ts index f5fd2ce39b4..3cf3c045bf3 100644 --- a/examples/auth/sveltekit/src/hooks.server.ts +++ b/examples/auth/sveltekit/src/hooks.server.ts @@ -8,7 +8,7 @@ export const handle: Handle = async ({ event, resolve }) => { getAll() { return event.cookies.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { /** * Note: You have to add the `path` variable to the * set and remove method due to sveltekit's cookie API @@ -18,6 +18,9 @@ export const handle: Handle = async ({ event, resolve }) => { cookiesToSet.forEach(({ name, value, options }) => event.cookies.set(name, value, { ...options, path: '/' }) ) + if (Object.keys(headers).length > 0) { + event.setHeaders(headers) + } }, }, }) diff --git a/examples/prompts/nextjs-supabase-auth.md b/examples/prompts/nextjs-supabase-auth.md index 910cf4f9b5a..ada3886c617 100644 --- a/examples/prompts/nextjs-supabase-auth.md +++ b/examples/prompts/nextjs-supabase-auth.md @@ -47,7 +47,7 @@ Instead, you MUST ALWAYS generate ONLY this pattern: getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { const response = NextResponse.next({ request, }) @@ -55,6 +55,9 @@ Instead, you MUST ALWAYS generate ONLY this pattern: cookiesToSet.forEach(({ name, value, options }) => { response.cookies.set(name, value, options) }) + Object.entries(headers).forEach(([key, value]) => + response.headers.set(key, value) + ) return response } @@ -99,7 +102,7 @@ export async function createClient() { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) @@ -135,14 +138,17 @@ export async function proxy(request: NextRequest) { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { - cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) + setAll(cookiesToSet, headers) { + cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, }) cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/realtime/nextjs-auth-presence/pages/index.tsx b/examples/realtime/nextjs-auth-presence/pages/index.tsx index 6291a514d33..c32baf6d380 100644 --- a/examples/realtime/nextjs-auth-presence/pages/index.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/index.tsx @@ -66,12 +66,13 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { value: value ?? '', })) }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { const existing = ctx.res.getHeader('Set-Cookie') ?? [] ctx.res.setHeader('Set-Cookie', [ ...(Array.isArray(existing) ? existing : [String(existing)]), ...cookiesToSet.map(({ name, value, options }) => serialize(name, value, options)), ]) + Object.entries(headers).forEach(([key, value]) => ctx.res.setHeader(key, value)) }, }, } diff --git a/examples/realtime/nextjs-auth-presence/pages/profile.tsx b/examples/realtime/nextjs-auth-presence/pages/profile.tsx index 1d5aae9b611..db6e651181e 100644 --- a/examples/realtime/nextjs-auth-presence/pages/profile.tsx +++ b/examples/realtime/nextjs-auth-presence/pages/profile.tsx @@ -28,12 +28,13 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { value: value ?? '', })) }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { const existing = ctx.res.getHeader('Set-Cookie') ?? [] ctx.res.setHeader('Set-Cookie', [ ...(Array.isArray(existing) ? existing : [String(existing)]), ...cookiesToSet.map(({ name, value, options }) => serialize(name, value, options)), ]) + Object.entries(headers).forEach(([key, value]) => ctx.res.setHeader(key, value)) }, }, } diff --git a/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts b/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts index 8c8c133b828..62b1ec997a0 100644 --- a/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts +++ b/examples/realtime/nextjs-authorization-demo/utils/supabase/proxy.ts @@ -15,12 +15,15 @@ export const updateSession = async (request: NextRequest) => { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, headers) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request }) cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts b/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts index cf32b1dfdc8..be2045a7d71 100644 --- a/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts +++ b/examples/realtime/nextjs-authorization-demo/utils/supabase/server.ts @@ -12,7 +12,7 @@ export const createClient = async () => { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) diff --git a/examples/user-management/nextjs-user-management/lib/supabase/proxy.ts b/examples/user-management/nextjs-user-management/lib/supabase/proxy.ts index 3e93ded72f8..0283eb5f293 100644 --- a/examples/user-management/nextjs-user-management/lib/supabase/proxy.ts +++ b/examples/user-management/nextjs-user-management/lib/supabase/proxy.ts @@ -14,14 +14,17 @@ export async function updateSession(request: NextRequest) { getAll() { return request.cookies.getAll() }, - setAll(cookiesToSet) { - cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) + setAll(cookiesToSet, headers) { + cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, }) cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ) + Object.entries(headers).forEach(([key, value]) => + supabaseResponse.headers.set(key, value) + ) }, }, } diff --git a/examples/user-management/nextjs-user-management/lib/supabase/server.ts b/examples/user-management/nextjs-user-management/lib/supabase/server.ts index 1a3500f790b..5171c710d06 100644 --- a/examples/user-management/nextjs-user-management/lib/supabase/server.ts +++ b/examples/user-management/nextjs-user-management/lib/supabase/server.ts @@ -14,7 +14,7 @@ export async function createClient() { getAll() { return cookieStore.getAll() }, - setAll(cookiesToSet) { + setAll(cookiesToSet, _headers) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) diff --git a/examples/user-management/sveltekit-user-management/src/hooks.server.ts b/examples/user-management/sveltekit-user-management/src/hooks.server.ts index c4fc1ed71a0..88538a8787a 100644 --- a/examples/user-management/sveltekit-user-management/src/hooks.server.ts +++ b/examples/user-management/sveltekit-user-management/src/hooks.server.ts @@ -13,10 +13,13 @@ export const handle: Handle = async ({ event, resolve }) => { * requiring this to be set, setting the path to `/` * will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies) */ - setAll: (cookiesToSet) => { + setAll: (cookiesToSet, headers) => { cookiesToSet.forEach(({ name, value, options }) => { event.cookies.set(name, value, { ...options, path: '/' }) }) + if (Object.keys(headers).length > 0) { + event.setHeaders(headers) + } }, }, })