From b71902b686b2ef1ca27d9ff300331a3b7d2f3845 Mon Sep 17 00:00:00 2001 From: emil Date: Fri, 15 May 2026 01:59:56 +0300 Subject: [PATCH] fix(oauth): use separate Set-Cookie headers for multiple cookies Browser cannot parse multiple cookies from a single Set-Cookie header joined by comma (RFC 6265). Use Headers.append() to send each cookie in its own Set-Cookie header. Fixes state/verifier cookie mismatch. --- src/pages/api/auth/callback/vk.ts | 15 ++++++--------- src/pages/api/auth/callback/yandex.ts | 15 ++++++--------- src/pages/api/auth/login/vk.ts | 13 +++++-------- src/pages/api/auth/login/yandex.ts | 13 +++++-------- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/pages/api/auth/callback/vk.ts b/src/pages/api/auth/callback/vk.ts index 62cbe50..ddce15c 100644 --- a/src/pages/api/auth/callback/vk.ts +++ b/src/pages/api/auth/callback/vk.ts @@ -106,17 +106,14 @@ export const GET: APIRoute = async ({ url, request }) => { expiresAt, }); - const cookies = [ - `${COOKIE_NAME}=${sessionToken}; HttpOnly; SameSite=Strict; Max-Age=604800; Path=/`, - `${VERIFIER_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`, - `oauth_state=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`, - ]; + const headers = new Headers(); + headers.set('Location', '/dm/'); + headers.append('Set-Cookie', `${COOKIE_NAME}=${sessionToken}; HttpOnly; SameSite=Strict; Max-Age=604800; Path=/`); + headers.append('Set-Cookie', `${VERIFIER_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`); + headers.append('Set-Cookie', `oauth_state=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`); return new Response(null, { status: 302, - headers: { - Location: '/dm/', - 'Set-Cookie': cookies.join(', '), - }, + headers, }); }; diff --git a/src/pages/api/auth/callback/yandex.ts b/src/pages/api/auth/callback/yandex.ts index 8a40ca9..4f6bfb3 100644 --- a/src/pages/api/auth/callback/yandex.ts +++ b/src/pages/api/auth/callback/yandex.ts @@ -107,17 +107,14 @@ export const GET: APIRoute = async ({ url, request }) => { expiresAt, }); - const cookies = [ - `${COOKIE_NAME}=${sessionToken}; HttpOnly; SameSite=Strict; Max-Age=604800; Path=/`, - `${VERIFIER_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`, - `oauth_state=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`, - ]; + const headers = new Headers(); + headers.set('Location', '/dm/'); + headers.append('Set-Cookie', `${COOKIE_NAME}=${sessionToken}; HttpOnly; SameSite=Strict; Max-Age=604800; Path=/`); + headers.append('Set-Cookie', `${VERIFIER_COOKIE_NAME}=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`); + headers.append('Set-Cookie', `oauth_state=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/`); return new Response(null, { status: 302, - headers: { - Location: '/dm/', - 'Set-Cookie': cookies.join(', '), - }, + headers, }); }; diff --git a/src/pages/api/auth/login/vk.ts b/src/pages/api/auth/login/vk.ts index 5c88279..ee1d783 100644 --- a/src/pages/api/auth/login/vk.ts +++ b/src/pages/api/auth/login/vk.ts @@ -22,16 +22,13 @@ export const GET: APIRoute = async ({ url }) => { const redirectUrl = `${vkOAuthConfig.authUrl}?${params.toString()}`; - const cookies = [ - `${VERIFIER_COOKIE_NAME}=${encodeURIComponent(verifier)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`, - `oauth_state=${encodeURIComponent(state)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`, - ]; + const headers = new Headers(); + headers.set('Location', redirectUrl); + headers.append('Set-Cookie', `${VERIFIER_COOKIE_NAME}=${encodeURIComponent(verifier)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`); + headers.append('Set-Cookie', `oauth_state=${encodeURIComponent(state)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`); return new Response(null, { status: 302, - headers: { - Location: redirectUrl, - 'Set-Cookie': cookies.join(', '), - }, + headers, }); }; diff --git a/src/pages/api/auth/login/yandex.ts b/src/pages/api/auth/login/yandex.ts index 8b7aceb..a032efb 100644 --- a/src/pages/api/auth/login/yandex.ts +++ b/src/pages/api/auth/login/yandex.ts @@ -22,16 +22,13 @@ export const GET: APIRoute = async ({ url }) => { const redirectUrl = `${yandexOAuthConfig.authUrl}?${params.toString()}`; - const cookies = [ - `${VERIFIER_COOKIE_NAME}=${encodeURIComponent(verifier)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`, - `oauth_state=${encodeURIComponent(state)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`, - ]; + const headers = new Headers(); + headers.set('Location', redirectUrl); + headers.append('Set-Cookie', `${VERIFIER_COOKIE_NAME}=${encodeURIComponent(verifier)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`); + headers.append('Set-Cookie', `oauth_state=${encodeURIComponent(state)}; HttpOnly; Secure; SameSite=Lax; Max-Age=600; Path=/`); return new Response(null, { status: 302, - headers: { - Location: redirectUrl, - 'Set-Cookie': cookies.join(', '), - }, + headers, }); };