From dfd8076c1665532b23ce41893e596eb01070278a Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 23 Sep 2026 08:08:22 -0700 Subject: [PATCH] fix(nuxt): preserve multiple Set-Cookie headers in clerkMiddleware --- .changeset/nuxt-append-set-cookie.md | 5 ++++ .../server/__tests__/clerkMiddleware.test.ts | 30 ++++++++++++++++++- .../src/runtime/server/clerkMiddleware.ts | 4 +-- .../nuxt/src/runtime/types/nitro-server.d.ts | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 .changeset/nuxt-append-set-cookie.md diff --git a/.changeset/nuxt-append-set-cookie.md b/.changeset/nuxt-append-set-cookie.md new file mode 100644 index 00000000000..907a293b146 --- /dev/null +++ b/.changeset/nuxt-append-set-cookie.md @@ -0,0 +1,5 @@ +--- +'@clerk/nuxt': patch +--- + +Fix `clerkMiddleware()` dropping all but the last `Set-Cookie` header when Clerk sets multiple cookies in one response, such as after a handshake or a session refresh. diff --git a/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts b/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts index 99fa714d0a1..04e75cba469 100644 --- a/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts +++ b/packages/nuxt/src/runtime/server/__tests__/clerkMiddleware.test.ts @@ -44,7 +44,7 @@ vi.mock('#imports', async () => { useRuntimeConfig: () => ({}), createError, eventHandler, - setResponseHeader, + appendResponseHeader: h3.appendResponseHeader, getRequestHeaders: h3.getRequestHeaders, getRequestProtocol: h3.getRequestProtocol, getRequestURL: h3.getRequestURL, @@ -113,6 +113,34 @@ describe('clerkMiddleware(params)', () => { expect(await response.json()).toEqual(SESSION_AUTH_RESPONSE); }); + test('preserves multiple Set-Cookie headers returned by authenticateRequest', async () => { + const authHeaders = new Headers(); + const cookies = [ + '__clerk_handshake=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax', + '__session=refreshed; Path=/; HttpOnly; SameSite=Lax', + ]; + cookies.forEach(cookie => authHeaders.append('set-cookie', cookie)); + authHeaders.set('x-clerk-auth-status', 'signed-in'); + authenticateRequestMock.mockResolvedValueOnce({ + toAuth: () => SESSION_AUTH_RESPONSE, + headers: authHeaders, + }); + + const app = createApp(); + const handler = toWebHandler(app); + app.use(clerkMiddleware()); + app.use( + '/', + eventHandler(event => event.context.auth()), + ); + + const response = await handler(new Request(new URL('/', 'http://localhost'))); + + expect(response.status).toBe(200); + expect(response.headers.getSetCookie()).toEqual(cookies); + expect(response.headers.get('x-clerk-auth-status')).toBe('signed-in'); + }); + test('executes handler and renders route when used with a custom handler', async () => { const app = createApp(); const handler = toWebHandler(app); diff --git a/packages/nuxt/src/runtime/server/clerkMiddleware.ts b/packages/nuxt/src/runtime/server/clerkMiddleware.ts index aae8e02305f..7042b43d7a9 100644 --- a/packages/nuxt/src/runtime/server/clerkMiddleware.ts +++ b/packages/nuxt/src/runtime/server/clerkMiddleware.ts @@ -5,7 +5,7 @@ import { isMalformedURLError } from '@clerk/shared/pathMatcher'; import type { PendingSessionOptions } from '@clerk/shared/types'; import type { EventHandler } from 'h3'; -import { createError, eventHandler, setResponseHeader, useRuntimeConfig } from '#imports'; +import { appendResponseHeader, createError, eventHandler, useRuntimeConfig } from '#imports'; import { canUseKeyless } from '../utils/feature-flags'; import { clerkClient } from './clerkClient'; @@ -137,7 +137,7 @@ export const clerkMiddleware: ClerkMiddleware = (...args: unknown[]) => { if (requestState.headers) { requestState.headers.forEach((value, key) => { - setResponseHeader(event, key, value); + appendResponseHeader(event, key, value); }); } diff --git a/packages/nuxt/src/runtime/types/nitro-server.d.ts b/packages/nuxt/src/runtime/types/nitro-server.d.ts index 054b17126fe..ffe78aab528 100644 --- a/packages/nuxt/src/runtime/types/nitro-server.d.ts +++ b/packages/nuxt/src/runtime/types/nitro-server.d.ts @@ -1,12 +1,12 @@ import type { H3Event } from 'h3'; export { + appendResponseHeader, createError, eventHandler, getRequestHeaders, getRequestProtocol, getRequestURL, - setResponseHeader, toWebRequest, } from 'h3'; export type { EventHandler, H3Event } from 'h3';