Skip to content
420 changes: 420 additions & 0 deletions src/server/base-path-logout-integration.test.ts

Large diffs are not rendered by default.

42 changes: 28 additions & 14 deletions src/server/chunked-cookies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ describe("Chunked Cookie Utils", () => {

// Check removal of non-chunked cookie
expect(resCookies.set).toHaveBeenCalledWith(name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});

Expand All @@ -193,13 +194,16 @@ describe("Chunked Cookie Utils", () => {
expect(resCookies.set).toHaveBeenCalledTimes(4);
expect(resCookies.set).toHaveBeenNthCalledWith(1, name, value, options);
expect(resCookies.set).toHaveBeenNthCalledWith(2, `${name}__1`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).toHaveBeenNthCalledWith(3, `${name}__0`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).toHaveBeenNthCalledWith(4, `${name}__2`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(reqCookies.set).toHaveBeenCalledTimes(1);
expect(reqCookies.set).toHaveBeenCalledWith(name, value);
Expand Down Expand Up @@ -244,7 +248,8 @@ describe("Chunked Cookie Utils", () => {
options
);
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(reqCookies.set).toHaveBeenCalledTimes(3);
});
Expand Down Expand Up @@ -335,7 +340,8 @@ describe("Chunked Cookie Utils", () => {
expect.objectContaining({ domain: "example.com" })
);
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});

Expand Down Expand Up @@ -405,7 +411,8 @@ describe("Chunked Cookie Utils", () => {
expectedOptions
);
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).not.toHaveBeenCalledWith(
expect.any(String),
Expand Down Expand Up @@ -478,7 +485,8 @@ describe("Chunked Cookie Utils", () => {
expectedOptions
);
expect(resCookies.set).toHaveBeenNthCalledWith(4, name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});

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

expect(resCookies.set).toHaveBeenCalledWith(name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});

Expand All @@ -572,20 +581,25 @@ describe("Chunked Cookie Utils", () => {
// Should delete main cookie and 3 chunks
expect(resCookies.set).toHaveBeenCalledTimes(4);
expect(resCookies.set).toHaveBeenCalledWith(name, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).toHaveBeenCalledWith(`${name}__0`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).toHaveBeenCalledWith(`${name}__1`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
expect(resCookies.set).toHaveBeenCalledWith(`${name}__2`, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
// Should not delete unrelated cookies
expect(resCookies.set).not.toHaveBeenCalledWith("otherCookie", "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});
});
Expand Down
10 changes: 8 additions & 2 deletions src/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ export class Auth0Client {
options.clientAssertionSigningAlg ||
process.env.AUTH0_CLIENT_ASSERTION_SIGNING_ALG;

// Auto-detect base path for cookie configuration
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;

const sessionCookieOptions: SessionCookieOptions = {
name: options.session?.cookie?.name ?? "__session",
secure:
Expand All @@ -236,7 +239,10 @@ export class Auth0Client {
(process.env.AUTH0_COOKIE_SAME_SITE as "lax" | "strict" | "none") ??
"lax",
path:
options.session?.cookie?.path ?? process.env.AUTH0_COOKIE_PATH ?? "/",
options.session?.cookie?.path ??
process.env.AUTH0_COOKIE_PATH ??
basePath ??
"/",
transient:
options.session?.cookie?.transient ??
process.env.AUTH0_COOKIE_TRANSIENT === "true",
Expand All @@ -247,7 +253,7 @@ export class Auth0Client {
prefix: options.transactionCookie?.prefix ?? "__txn_",
secure: options.transactionCookie?.secure ?? false,
sameSite: options.transactionCookie?.sameSite ?? "lax",
path: options.transactionCookie?.path ?? "/"
path: options.transactionCookie?.path ?? basePath ?? "/"
};

if (appBaseUrl) {
Expand Down
22 changes: 14 additions & 8 deletions src/server/cookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export function setChunkedCookie(

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

Expand Down Expand Up @@ -256,13 +256,13 @@ export function setChunkedCookie(
for (let i = 0; i < chunksToRemove; i++) {
const chunkIndexToRemove = chunkIndex + i;
const chunkName = `${name}${CHUNK_PREFIX}${chunkIndexToRemove}`;
deleteCookie(resCookies, chunkName);
deleteCookie(resCookies, chunkName, finalOptions.path);
reqCookies.delete(chunkName);
}
}

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

Expand Down Expand Up @@ -328,13 +328,14 @@ export function deleteChunkedCookie(
name: string,
reqCookies: RequestCookies,
resCookies: ResponseCookies,
isLegacyCookie?: boolean
isLegacyCookie?: boolean,
path?: string
): void {
// Delete main cookie
deleteCookie(resCookies, name);
deleteCookie(resCookies, name, path);

getAllChunkedCookies(reqCookies, name, isLegacyCookie).forEach((cookie) => {
deleteCookie(resCookies, cookie.name); // Delete each filtered cookie
deleteCookie(resCookies, cookie.name, path); // Delete each filtered cookie
});
}

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

export function deleteCookie(resCookies: ResponseCookies, name: string) {
export function deleteCookie(
resCookies: ResponseCookies,
name: string,
path?: string
) {
resCookies.set(name, "", {
maxAge: 0 // Ensure the cookie is deleted immediately
maxAge: 0, // Ensure the cookie is deleted immediately
path: path || "/"
});
}
3 changes: 2 additions & 1 deletion src/server/session/stateful-session-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,8 @@ describe("Stateful Session Store", async () => {
await sessionStore.set(requestCookies, responseCookies, session);

expect(responseCookies.set).toHaveBeenCalledWith(LEGACY_COOKIE_NAME, "", {
maxAge: 0
maxAge: 0,
path: "/"
});
});

Expand Down
12 changes: 10 additions & 2 deletions src/server/session/stateful-session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,11 @@ export class StatefulSessionStore extends AbstractSessionStore {
this.sessionCookieName !== LEGACY_COOKIE_NAME &&
reqCookies.has(LEGACY_COOKIE_NAME)
) {
cookies.deleteCookie(resCookies, LEGACY_COOKIE_NAME);
cookies.deleteCookie(
resCookies,
LEGACY_COOKIE_NAME,
this.cookieConfig.path
);
}
}

Expand All @@ -177,7 +181,11 @@ export class StatefulSessionStore extends AbstractSessionStore {
resCookies: cookies.ResponseCookies
) {
const cookieValue = reqCookies.get(this.sessionCookieName)?.value;
cookies.deleteCookie(resCookies, this.sessionCookieName);
cookies.deleteCookie(
resCookies,
this.sessionCookieName,
this.cookieConfig.path
);

if (!cookieValue) {
return;
Expand Down
16 changes: 9 additions & 7 deletions src/server/session/stateless-session-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ describe("Stateless Session Store", async () => {
LEGACY_COOKIE_NAME,
"",
{
maxAge: 0
maxAge: 0,
path: "/"
}
);
});
Expand Down Expand Up @@ -488,25 +489,25 @@ describe("Stateless Session Store", async () => {
1,
"__session",
expect.any(String),
expect.not.objectContaining({ maxAge: 0 })
expect.not.objectContaining({ maxAge: 0, path: "/" })
);
expect(responseCookies.set).toHaveBeenNthCalledWith(
2,
LEGACY_COOKIE_NAME,
"",
{ maxAge: 0 }
{ maxAge: 0, path: "/" }
);
expect(responseCookies.set).toHaveBeenNthCalledWith(
3,
`${LEGACY_COOKIE_NAME}.0`,
"",
{ maxAge: 0 }
{ maxAge: 0, path: "/" }
);
expect(responseCookies.set).toHaveBeenNthCalledWith(
4,
`${LEGACY_COOKIE_NAME}.1`,
"",
{ maxAge: 0 }
{ maxAge: 0, path: "/" }
);
});
});
Expand Down Expand Up @@ -784,14 +785,15 @@ describe("Stateless Session Store", async () => {
1,
"__session",
expect.any(String),
expect.not.objectContaining({ maxAge: 0 })
expect.not.objectContaining({ maxAge: 0, path: "/" })
);
expect(setSpy).toHaveBeenNthCalledWith(
2,
legacyCookiesInSetup[0].name,
"",
{
maxAge: 0
maxAge: 0,
path: "/"
}
);
});
Expand Down
13 changes: 10 additions & 3 deletions src/server/session/stateless-session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,25 @@ export class StatelessSessionStore extends AbstractSessionStore {
LEGACY_COOKIE_NAME,
reqCookies,
resCookies,
true
true,
this.cookieConfig.path
);
}

async delete(
reqCookies: cookies.RequestCookies,
resCookies: cookies.ResponseCookies
) {
cookies.deleteChunkedCookie(this.sessionCookieName, reqCookies, resCookies);
cookies.deleteChunkedCookie(
this.sessionCookieName,
reqCookies,
resCookies,
false,
this.cookieConfig.path
);

this.getConnectionTokenSetsCookies(reqCookies).forEach((cookie) =>
cookies.deleteCookie(resCookies, cookie.name)
cookies.deleteCookie(resCookies, cookie.name, this.cookieConfig.path)
);
}

Expand Down
8 changes: 6 additions & 2 deletions src/server/transaction-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ export class TransactionStore {
}

async delete(resCookies: cookies.ResponseCookies, state: string) {
cookies.deleteCookie(resCookies, this.getTransactionCookieName(state));
cookies.deleteCookie(
resCookies,
this.getTransactionCookieName(state),
this.cookieConfig.path
);
}

/**
Expand All @@ -131,7 +135,7 @@ export class TransactionStore {
const txnPrefix = this.getCookiePrefix();
reqCookies.getAll().forEach((cookie) => {
if (cookie.name.startsWith(txnPrefix)) {
cookies.deleteCookie(resCookies, cookie.name);
cookies.deleteCookie(resCookies, cookie.name, this.cookieConfig.path);
}
});
}
Expand Down