Skip to content

Commit 2df00f2

Browse files
bugfix: Clear cookies with the correct path when basePath is used (#2232)
1 parent 2cfe390 commit 2df00f2

9 files changed

+507
-39
lines changed

src/server/base-path-logout-integration.test.ts

Lines changed: 420 additions & 0 deletions
Large diffs are not rendered by default.

src/server/chunked-cookies.test.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ describe("Chunked Cookie Utils", () => {
170170

171171
// Check removal of non-chunked cookie
172172
expect(resCookies.set).toHaveBeenCalledWith(name, "", {
173-
maxAge: 0
173+
maxAge: 0,
174+
path: "/"
174175
});
175176
});
176177

@@ -193,13 +194,16 @@ describe("Chunked Cookie Utils", () => {
193194
expect(resCookies.set).toHaveBeenCalledTimes(4);
194195
expect(resCookies.set).toHaveBeenNthCalledWith(1, name, value, options);
195196
expect(resCookies.set).toHaveBeenNthCalledWith(2, `${name}__1`, "", {
196-
maxAge: 0
197+
maxAge: 0,
198+
path: "/"
197199
});
198200
expect(resCookies.set).toHaveBeenNthCalledWith(3, `${name}__0`, "", {
199-
maxAge: 0
201+
maxAge: 0,
202+
path: "/"
200203
});
201204
expect(resCookies.set).toHaveBeenNthCalledWith(4, `${name}__2`, "", {
202-
maxAge: 0
205+
maxAge: 0,
206+
path: "/"
203207
});
204208
expect(reqCookies.set).toHaveBeenCalledTimes(1);
205209
expect(reqCookies.set).toHaveBeenCalledWith(name, value);
@@ -244,7 +248,8 @@ describe("Chunked Cookie Utils", () => {
244248
options
245249
);
246250
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
247-
maxAge: 0
251+
maxAge: 0,
252+
path: "/"
248253
});
249254
expect(reqCookies.set).toHaveBeenCalledTimes(3);
250255
});
@@ -335,7 +340,8 @@ describe("Chunked Cookie Utils", () => {
335340
expect.objectContaining({ domain: "example.com" })
336341
);
337342
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
338-
maxAge: 0
343+
maxAge: 0,
344+
path: "/"
339345
});
340346
});
341347

@@ -405,7 +411,8 @@ describe("Chunked Cookie Utils", () => {
405411
expectedOptions
406412
);
407413
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
408-
maxAge: 0
414+
maxAge: 0,
415+
path: "/"
409416
});
410417
expect(resCookies.set).not.toHaveBeenCalledWith(
411418
expect.any(String),
@@ -478,7 +485,8 @@ describe("Chunked Cookie Utils", () => {
478485
expectedOptions
479486
);
480487
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
481-
maxAge: 0
488+
maxAge: 0,
489+
path: "/"
482490
});
483491
});
484492

@@ -552,7 +560,8 @@ describe("Chunked Cookie Utils", () => {
552560
deleteChunkedCookie(name, reqCookies, resCookies);
553561

554562
expect(resCookies.set).toHaveBeenCalledWith(name, "", {
555-
maxAge: 0
563+
maxAge: 0,
564+
path: "/"
556565
});
557566
});
558567

@@ -572,20 +581,25 @@ describe("Chunked Cookie Utils", () => {
572581
// Should delete main cookie and 3 chunks
573582
expect(resCookies.set).toHaveBeenCalledTimes(4);
574583
expect(resCookies.set).toHaveBeenCalledWith(name, "", {
575-
maxAge: 0
584+
maxAge: 0,
585+
path: "/"
576586
});
577587
expect(resCookies.set).toHaveBeenCalledWith(`${name}__0`, "", {
578-
maxAge: 0
588+
maxAge: 0,
589+
path: "/"
579590
});
580591
expect(resCookies.set).toHaveBeenCalledWith(`${name}__1`, "", {
581-
maxAge: 0
592+
maxAge: 0,
593+
path: "/"
582594
});
583595
expect(resCookies.set).toHaveBeenCalledWith(`${name}__2`, "", {
584-
maxAge: 0
596+
maxAge: 0,
597+
path: "/"
585598
});
586599
// Should not delete unrelated cookies
587600
expect(resCookies.set).not.toHaveBeenCalledWith("otherCookie", "", {
588-
maxAge: 0
601+
maxAge: 0,
602+
path: "/"
589603
});
590604
});
591605
});

src/server/client.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ export class Auth0Client {
226226
options.clientAssertionSigningAlg ||
227227
process.env.AUTH0_CLIENT_ASSERTION_SIGNING_ALG;
228228

229+
// Auto-detect base path for cookie configuration
230+
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
231+
229232
const sessionCookieOptions: SessionCookieOptions = {
230233
name: options.session?.cookie?.name ?? "__session",
231234
secure:
@@ -236,7 +239,10 @@ export class Auth0Client {
236239
(process.env.AUTH0_COOKIE_SAME_SITE as "lax" | "strict" | "none") ??
237240
"lax",
238241
path:
239-
options.session?.cookie?.path ?? process.env.AUTH0_COOKIE_PATH ?? "/",
242+
options.session?.cookie?.path ??
243+
process.env.AUTH0_COOKIE_PATH ??
244+
basePath ??
245+
"/",
240246
transient:
241247
options.session?.cookie?.transient ??
242248
process.env.AUTH0_COOKIE_TRANSIENT === "true",
@@ -247,7 +253,7 @@ export class Auth0Client {
247253
prefix: options.transactionCookie?.prefix ?? "__txn_",
248254
secure: options.transactionCookie?.secure ?? false,
249255
sameSite: options.transactionCookie?.sameSite ?? "lax",
250-
path: options.transactionCookie?.path ?? "/"
256+
path: options.transactionCookie?.path ?? basePath ?? "/"
251257
};
252258

253259
if (appBaseUrl) {

src/server/cookies.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ export function setChunkedCookie(
226226

227227
// When we are writing a non-chunked cookie, we should remove the chunked cookies
228228
getAllChunkedCookies(reqCookies, name).forEach((cookieChunk) => {
229-
deleteCookie(resCookies, cookieChunk.name);
229+
deleteCookie(resCookies, cookieChunk.name, finalOptions.path);
230230
reqCookies.delete(cookieChunk.name);
231231
});
232232

@@ -256,13 +256,13 @@ export function setChunkedCookie(
256256
for (let i = 0; i < chunksToRemove; i++) {
257257
const chunkIndexToRemove = chunkIndex + i;
258258
const chunkName = `${name}${CHUNK_PREFIX}${chunkIndexToRemove}`;
259-
deleteCookie(resCookies, chunkName);
259+
deleteCookie(resCookies, chunkName, finalOptions.path);
260260
reqCookies.delete(chunkName);
261261
}
262262
}
263263

264264
// When we have written chunked cookies, we should remove the non-chunked cookie
265-
deleteCookie(resCookies, name);
265+
deleteCookie(resCookies, name, finalOptions.path);
266266
reqCookies.delete(name);
267267
}
268268

@@ -328,13 +328,14 @@ export function deleteChunkedCookie(
328328
name: string,
329329
reqCookies: RequestCookies,
330330
resCookies: ResponseCookies,
331-
isLegacyCookie?: boolean
331+
isLegacyCookie?: boolean,
332+
path?: string
332333
): void {
333334
// Delete main cookie
334-
deleteCookie(resCookies, name);
335+
deleteCookie(resCookies, name, path);
335336

336337
getAllChunkedCookies(reqCookies, name, isLegacyCookie).forEach((cookie) => {
337-
deleteCookie(resCookies, cookie.name); // Delete each filtered cookie
338+
deleteCookie(resCookies, cookie.name, path); // Delete each filtered cookie
338339
});
339340
}
340341

@@ -357,8 +358,13 @@ export function addCacheControlHeadersForSession(res: NextResponse): void {
357358
res.headers.set("Expires", "0");
358359
}
359360

360-
export function deleteCookie(resCookies: ResponseCookies, name: string) {
361+
export function deleteCookie(
362+
resCookies: ResponseCookies,
363+
name: string,
364+
path?: string
365+
) {
361366
resCookies.set(name, "", {
362-
maxAge: 0 // Ensure the cookie is deleted immediately
367+
maxAge: 0, // Ensure the cookie is deleted immediately
368+
path: path || "/"
363369
});
364370
}

src/server/session/stateful-session-store.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,8 @@ describe("Stateful Session Store", async () => {
759759
await sessionStore.set(requestCookies, responseCookies, session);
760760

761761
expect(responseCookies.set).toHaveBeenCalledWith(LEGACY_COOKIE_NAME, "", {
762-
maxAge: 0
762+
maxAge: 0,
763+
path: "/"
763764
});
764765
});
765766

src/server/session/stateful-session-store.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ export class StatefulSessionStore extends AbstractSessionStore {
168168
this.sessionCookieName !== LEGACY_COOKIE_NAME &&
169169
reqCookies.has(LEGACY_COOKIE_NAME)
170170
) {
171-
cookies.deleteCookie(resCookies, LEGACY_COOKIE_NAME);
171+
cookies.deleteCookie(
172+
resCookies,
173+
LEGACY_COOKIE_NAME,
174+
this.cookieConfig.path
175+
);
172176
}
173177
}
174178

@@ -177,7 +181,11 @@ export class StatefulSessionStore extends AbstractSessionStore {
177181
resCookies: cookies.ResponseCookies
178182
) {
179183
const cookieValue = reqCookies.get(this.sessionCookieName)?.value;
180-
cookies.deleteCookie(resCookies, this.sessionCookieName);
184+
cookies.deleteCookie(
185+
resCookies,
186+
this.sessionCookieName,
187+
this.cookieConfig.path
188+
);
181189

182190
if (!cookieValue) {
183191
return;

src/server/session/stateless-session-store.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,8 @@ describe("Stateless Session Store", async () => {
447447
LEGACY_COOKIE_NAME,
448448
"",
449449
{
450-
maxAge: 0
450+
maxAge: 0,
451+
path: "/"
451452
}
452453
);
453454
});
@@ -488,25 +489,25 @@ describe("Stateless Session Store", async () => {
488489
1,
489490
"__session",
490491
expect.any(String),
491-
expect.not.objectContaining({ maxAge: 0 })
492+
expect.not.objectContaining({ maxAge: 0, path: "/" })
492493
);
493494
expect(responseCookies.set).toHaveBeenNthCalledWith(
494495
2,
495496
LEGACY_COOKIE_NAME,
496497
"",
497-
{ maxAge: 0 }
498+
{ maxAge: 0, path: "/" }
498499
);
499500
expect(responseCookies.set).toHaveBeenNthCalledWith(
500501
3,
501502
`${LEGACY_COOKIE_NAME}.0`,
502503
"",
503-
{ maxAge: 0 }
504+
{ maxAge: 0, path: "/" }
504505
);
505506
expect(responseCookies.set).toHaveBeenNthCalledWith(
506507
4,
507508
`${LEGACY_COOKIE_NAME}.1`,
508509
"",
509-
{ maxAge: 0 }
510+
{ maxAge: 0, path: "/" }
510511
);
511512
});
512513
});
@@ -784,14 +785,15 @@ describe("Stateless Session Store", async () => {
784785
1,
785786
"__session",
786787
expect.any(String),
787-
expect.not.objectContaining({ maxAge: 0 })
788+
expect.not.objectContaining({ maxAge: 0, path: "/" })
788789
);
789790
expect(setSpy).toHaveBeenNthCalledWith(
790791
2,
791792
legacyCookiesInSetup[0].name,
792793
"",
793794
{
794-
maxAge: 0
795+
maxAge: 0,
796+
path: "/"
795797
}
796798
);
797799
});

src/server/session/stateless-session-store.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,25 @@ export class StatelessSessionStore extends AbstractSessionStore {
140140
LEGACY_COOKIE_NAME,
141141
reqCookies,
142142
resCookies,
143-
true
143+
true,
144+
this.cookieConfig.path
144145
);
145146
}
146147

147148
async delete(
148149
reqCookies: cookies.RequestCookies,
149150
resCookies: cookies.ResponseCookies
150151
) {
151-
cookies.deleteChunkedCookie(this.sessionCookieName, reqCookies, resCookies);
152+
cookies.deleteChunkedCookie(
153+
this.sessionCookieName,
154+
reqCookies,
155+
resCookies,
156+
false,
157+
this.cookieConfig.path
158+
);
152159

153160
this.getConnectionTokenSetsCookies(reqCookies).forEach((cookie) =>
154-
cookies.deleteCookie(resCookies, cookie.name)
161+
cookies.deleteCookie(resCookies, cookie.name, this.cookieConfig.path)
155162
);
156163
}
157164

src/server/transaction-store.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ export class TransactionStore {
118118
}
119119

120120
async delete(resCookies: cookies.ResponseCookies, state: string) {
121-
cookies.deleteCookie(resCookies, this.getTransactionCookieName(state));
121+
cookies.deleteCookie(
122+
resCookies,
123+
this.getTransactionCookieName(state),
124+
this.cookieConfig.path
125+
);
122126
}
123127

124128
/**
@@ -131,7 +135,7 @@ export class TransactionStore {
131135
const txnPrefix = this.getCookiePrefix();
132136
reqCookies.getAll().forEach((cookie) => {
133137
if (cookie.name.startsWith(txnPrefix)) {
134-
cookies.deleteCookie(resCookies, cookie.name);
138+
cookies.deleteCookie(resCookies, cookie.name, this.cookieConfig.path);
135139
}
136140
});
137141
}

0 commit comments

Comments
 (0)