Skip to content

Commit a694b80

Browse files
committed
fix: cookie decoding for Node and Cloudflare
1 parent ab492ca commit a694b80

File tree

9 files changed

+49
-24
lines changed

9 files changed

+49
-24
lines changed

.changeset/hot-chicken-ring.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
fix cookie decoding for Node and Cloudflare

packages/open-next/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"@tsconfig/node18": "^1.0.1",
4949
"aws4fetch": "^1.0.18",
5050
"chalk": "^5.3.0",
51+
"cookie": "^1.0.2",
5152
"esbuild": "catalog:",
5253
"express": "5.0.1",
5354
"path-to-regexp": "^6.3.0",

packages/open-next/src/http/openNextResponse.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Transform } from "node:stream";
1010

1111
import type { StreamCreator } from "types/open-next";
1212
import { debug } from "../adapters/logger";
13-
import { parseCookies, parseHeaders } from "./util";
13+
import { parseHeaders, parseSetCookieHeader } from "./util";
1414

1515
const SET_COOKIE_HEADER = "set-cookie";
1616
const CANNOT_BE_USED = "This cannot be used in OpenNext";
@@ -152,7 +152,7 @@ export class OpenNextNodeResponse extends Transform implements ServerResponse {
152152
...this.initialHeaders,
153153
...this.headers,
154154
};
155-
const initialCookies = parseCookies(
155+
const initialCookies = parseSetCookieHeader(
156156
this.initialHeaders[SET_COOKIE_HEADER]?.toString(),
157157
);
158158
this._cookies =

packages/open-next/src/http/util.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ export const convertHeader = (header: http.OutgoingHttpHeader) => {
2828
return String(header);
2929
};
3030

31-
export function parseCookies(
31+
/**
32+
* Parses a (coma-separated) list of Set-Cookie headers
33+
*
34+
* @param cookies A coma-separated list of Set-Cookie headers or a list of Set-Cookie header
35+
* @returns A list of Set-Cookie header
36+
*/
37+
export function parseSetCookieHeader(
3238
cookies: string | string[] | null | undefined,
3339
): string[] {
3440
if (!cookies) {

packages/open-next/src/overrides/converters/aws-apigw-v2.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {
22
APIGatewayProxyEventV2,
33
APIGatewayProxyResultV2,
44
} from "aws-lambda";
5-
import { parseCookies } from "http/util";
5+
import { parseSetCookieHeader } from "http/util";
66
import type { InternalEvent, InternalResult } from "types/open-next";
77
import type { Converter } from "types/overrides";
88
import { fromReadableStream } from "utils/stream";
@@ -122,7 +122,7 @@ async function convertToApiGatewayProxyResultV2(
122122
const response: APIGatewayProxyResultV2 = {
123123
statusCode: result.statusCode,
124124
headers,
125-
cookies: parseCookies(result.headers["set-cookie"]),
125+
cookies: parseSetCookieHeader(result.headers["set-cookie"]),
126126
body,
127127
isBase64Encoded: result.isBase64Encoded,
128128
};

packages/open-next/src/overrides/converters/aws-cloudfront.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
CloudFrontRequestEvent,
88
CloudFrontRequestResult,
99
} from "aws-lambda";
10-
import { parseCookies } from "http/util";
10+
import { parseSetCookieHeader } from "http/util";
1111
import type {
1212
InternalEvent,
1313
InternalResult,
@@ -133,10 +133,12 @@ function convertToCloudfrontHeaders(
133133
)
134134
.forEach(([key, value]) => {
135135
if (key === "set-cookie") {
136-
cloudfrontHeaders[key] = parseCookies(`${value}`).map((cookie) => ({
137-
key,
138-
value: cookie,
139-
}));
136+
cloudfrontHeaders[key] = parseSetCookieHeader(`${value}`).map(
137+
(cookie) => ({
138+
key,
139+
value: cookie,
140+
}),
141+
);
140142
return;
141143
}
142144

packages/open-next/src/overrides/converters/edge.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Buffer } from "node:buffer";
22

3-
import { parseCookies } from "http/util";
3+
import cookieParser from "cookie";
4+
import { parseSetCookieHeader } from "http/util";
45
import type {
56
InternalEvent,
67
InternalResult,
@@ -29,11 +30,11 @@ const converter: Converter<InternalEvent, InternalResult | MiddlewareResult> = {
2930
const rawPath = url.pathname;
3031
const method = event.method;
3132
const shouldHaveBody = method !== "GET" && method !== "HEAD";
32-
const cookies: Record<string, string> = Object.fromEntries(
33-
parseCookies(event.headers.get("cookie")).map((cookie) =>
34-
cookie.split("="),
35-
),
36-
);
33+
34+
const cookieHeader = event.headers.get("cookie");
35+
const cookies = cookieHeader
36+
? (cookieParser.parse(cookieHeader) as Record<string, string>)
37+
: {};
3738

3839
return {
3940
type: "core",
@@ -81,7 +82,7 @@ const converter: Converter<InternalEvent, InternalResult | MiddlewareResult> = {
8182
if (key === "set-cookie" && typeof value === "string") {
8283
// If the value is a string, we need to parse it into an array
8384
// This is the case for middleware direct result
84-
const cookies = parseCookies(value);
85+
const cookies = parseSetCookieHeader(value);
8586
for (const cookie of cookies) {
8687
headers.append(key, cookie);
8788
}

packages/open-next/src/overrides/converters/node.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { IncomingMessage } from "node:http";
22

3-
import { parseCookies } from "http/util";
3+
import cookieParser from "cookie";
44
import type { InternalResult } from "types/open-next";
55
import type { Converter } from "types/overrides";
66
import { extractHostFromHeaders, getQueryFromSearchParams } from "./utils.js";
@@ -30,6 +30,12 @@ const converter: Converter = {
3030
`${req.protocol ? req.protocol : "http"}://${extractHostFromHeaders(headers)}${req.url}`,
3131
);
3232
const query = getQueryFromSearchParams(url.searchParams);
33+
34+
const cookieHeader = req.headers.cookie;
35+
const cookies = cookieHeader
36+
? (cookieParser.parse(cookieHeader) as Record<string, string>)
37+
: {};
38+
3339
return {
3440
type: "core",
3541
method: req.method ?? "GET",
@@ -42,12 +48,7 @@ const converter: Converter = {
4248
req.socket.remoteAddress ??
4349
"::1",
4450
query,
45-
cookies: Object.fromEntries(
46-
parseCookies(req.headers.cookie)?.map((cookie) => {
47-
const [key, value] = cookie.split("=");
48-
return [key, value];
49-
}) ?? [],
50-
),
51+
cookies,
5152
};
5253
},
5354
// Nothing to do here, it's streaming

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)