Skip to content

Feat/new session store#406

Open
pesickaa wants to merge 34 commits into
kinde-oss:mainfrom
pesickaa:feat/new-session-store
Open

Feat/new session store#406
pesickaa wants to merge 34 commits into
kinde-oss:mainfrom
pesickaa:feat/new-session-store

Conversation

@pesickaa
Copy link
Copy Markdown
Contributor

@pesickaa pesickaa commented Oct 3, 2025

Explain your changes

Create new NextJs cookie store for the app router.
Ensure new cookie store is testable.

Checklist

🛟 If you need help, consider asking for advice over in the Kinde community.

@pesickaa pesickaa self-assigned this Oct 3, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 3, 2025

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new CookieStorage session manager that stores session state in Next.js cookies for both App Router and middleware contexts. Supports chunked storage for large values, lazy cookie store selection based on request context, and includes comprehensive test coverage of read, write, remove, and destroy operations.

Changes

Cookie Storage Session Manager

Layer / File(s) Summary
Foundation: Imports, settings, and class structure
src/session/sessionManager/cookieManager.ts (lines 1–43)
Imports session utilities and Next.js cookie APIs, exports cookieStorageSettings with key prefix and chunk-size configuration, and defines the CookieStorage<V> class extending SessionBase with public req/resp handles and a persistent flag.
Cookie store access: Middleware detection and lazy initialization
src/session/sessionManager/cookieManager.ts (lines 45–104)
Helper functions detect middleware context and decide cookie deletion based on key prefix and COOKIE_LIST matching; lazy ensureCookieStore() selects between middleware req.cookies, App Router cookies(), and throws on unsupported contexts.
Core session operations: Read, write, remove, and destroy
src/session/sessionManager/cookieManager.ts (lines 110–236)
destroySession() clears matching cookies, setSessionItem() serializes and chunks values with TTL-based options, getSessionItem() reassembles chunks and parses with fallback error handling, removeSessionItem() deletes all cookie parts for a key.
Batch operations: Set and remove multiple items
src/session/sessionManager/cookieManager.ts (lines 241–254)
setItems() and removeItems() sequentially delegate to single-item operations for multiple entries.
Test infrastructure: FakeCookieStore and test setup
tests/frontend/cookie-manager.test.ts (lines 1–50)
FakeCookieStore test double implements minimal get/has/set/delete/getAll with maxAge <= 0 as deletion; test suite injects the mock into CookieStorage before each test.
Core operation tests: Read, write, and remove behaviors
tests/frontend/cookie-manager.test.ts (lines 52–107)
Tests verify set/get round-tripping with expected "kinde-" prefix, null for missing keys, chunking and reassembly of long values, destr parsing of primitives, and complete removal of all cookie chunks.
Advanced tests: Destroy, objects, and edge cases
tests/frontend/cookie-manager.test.ts (lines 108–193)
Tests verify session destruction (clearing COOKIE_LIST prefixes while preserving unrelated cookies), non-persistent cookies, object round-tripping, undefined value handling, and enforcement of the "kinde-" prefix convention.

Sequence Diagram

sequenceDiagram
  participant RequestHandler
  participant CookieStorage
  participant CookieStore
  participant ClientCookies

  RequestHandler->>CookieStorage: new CookieStorage(req, resp, options)
  RequestHandler->>CookieStorage: setSessionItem(key, value)
  CookieStorage->>CookieStorage: ensureCookieStore() - select middleware or server
  CookieStorage->>CookieStorage: serialize and chunk value
  CookieStorage->>CookieStore: set prefixed chunk cookies
  CookieStore->>ClientCookies: write via req/resp or cookies()
  
  RequestHandler->>CookieStorage: getSessionItem(key)
  CookieStorage->>CookieStore: read base cookie + chunks
  CookieStore-->>CookieStorage: concatenated chunk data
  CookieStorage->>CookieStorage: parse via destr
  CookieStorage-->>RequestHandler: value or null
  
  RequestHandler->>CookieStorage: destroySession()
  CookieStorage->>CookieStore: enumerate & delete matching cookies
  CookieStore->>ClientCookies: clear via maxAge: 0
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/new session store' is vague and uses generic phrasing that doesn't convey meaningful detail about the specific changes, such as cookie-based storage or App Router support. Consider a more descriptive title like 'Add Next.js cookie-based session manager for App Router' to clearly indicate what was implemented.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly relates to the changeset by mentioning creating a new Next.js cookie store for the app router and ensuring it is testable, which aligns with the actual changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8599cc and c809a17.

📒 Files selected for processing (5)
  • src/session/sessionManager/cookieManager.ts (1 hunks)
  • src/session/sessionManager/index.ts (1 hunks)
  • src/session/sessionManager/settings.ts (1 hunks)
  • src/session/sessionManager/types.ts (1 hunks)
  • tests/frontend/cookie-manager.test.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-13T10:45:31.961Z
Learnt from: DanielRivers
PR: kinde-oss/kinde-auth-nextjs#229
File: src/session/sessionManager.js:165-188
Timestamp: 2024-11-13T10:45:31.961Z
Learning: There's a new implementation coming soon for `src/session/sessionManager.js`, so refactoring suggestions related to cookie parsing logic may not be necessary at this time.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
🧬 Code graph analysis (4)
src/session/sessionManager/types.ts (1)
src/session/sessionManager/index.ts (2)
  • StorageKeys (8-8)
  • SessionManager (9-9)
tests/frontend/cookie-manager.test.ts (3)
src/session/sessionManager.js (1)
  • options (27-27)
src/session/sessionManager/cookieManager.ts (1)
  • CookieStorage (24-146)
src/utils/constants.ts (2)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
src/session/sessionManager/settings.ts (1)
src/session/sessionManager/types.ts (1)
  • StorageSettingsType (15-27)
src/session/sessionManager/cookieManager.ts (4)
src/session/sessionManager/settings.ts (1)
  • CookieStorageSettings (44-49)
src/utils/constants.ts (4)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
  • GLOBAL_COOKIE_OPTIONS (5-10)
  • TWENTY_NINE_DAYS (3-3)
src/session/sessionManager/types.ts (1)
  • SessionManager (61-105)
src/session/sessionManager.js (1)
  • options (27-27)
🪛 GitHub Actions: Build and test
src/session/sessionManager/index.ts

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

tests/frontend/cookie-manager.test.ts

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

src/session/sessionManager/settings.ts

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

src/session/sessionManager/cookieManager.ts

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

Comment thread src/session/sessionManager/cookieManager.ts Outdated
Comment thread src/session/sessionManager/index.ts Outdated
@pesickaa pesickaa requested a review from DanielRivers October 3, 2025 11:18
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/session/sessionManager/cookieManager.ts (1)

33-42: Fix constructor typing to allow session cookies.

The constructor's options: { persistent: true } type prevents callers from passing persistent: false, breaking session cookie functionality.

Apply this diff:

  constructor(
-    req: NextApiRequest,
-    resp: NextApiResponse,
-    options: { persistent: true },
+    req: NextApiRequest | undefined,
+    resp: NextApiResponse | undefined,
+    options: { persistent: boolean } = { persistent: true },
  ) {
    super();
    this.req = req;
    this.resp = resp;
    this.sessionState = options;
  }
🧹 Nitpick comments (4)
tests/frontend/cookie-manager.test.ts (1)

42-50: Consider testing session cookie behavior.

The test setup always uses persistent: true. Consider adding tests for persistent: false to verify session cookie behavior (no maxAge set).

For example:

it("creates session cookies when persistent is false", async () => {
  const sessionStorage = new CookieStorage<any>(undefined as any, undefined as any, {
    persistent: false,
  });
  // @ts-ignore
  sessionStorage._cookieStore = fake as any;
  
  await sessionStorage.setSessionItem(StorageKeys.state, "test");
  
  // Verify cookie was set without maxAge (session cookie)
  const cookie = fake.get(StorageKeys.state);
  expect(cookie).toBeDefined();
  // In real implementation, session cookies have undefined maxAge
});
src/session/sessionManager/cookieManager.ts (3)

69-83: Refactor cookie name extraction for clarity.

The current pattern chains .map() and .forEach() on separate lines. Consider combining into a single iteration for better readability.

Apply this diff:

  async destroySession(): Promise<void> {
    const cookieStore = await this.ensureCookieStore();
-    cookieStore
-      .getAll()
-      .map((c) => c.name)
-      .forEach((key) => {
-        if (COOKIE_LIST.some((substr) => key.startsWith(substr))) {
-          cookieStore.set(key, "", {
-            domain: config.cookieDomain ? config.cookieDomain : undefined,
-            maxAge: 0,
-            ...GLOBAL_COOKIE_OPTIONS,
-          });
-        }
-      });
+    for (const { name } of cookieStore.getAll()) {
+      if (COOKIE_LIST.some((substr) => name.startsWith(substr))) {
+        cookieStore.set(name, "", {
+          domain: config.cookieDomain ? config.cookieDomain : undefined,
+          maxAge: 0,
+          ...GLOBAL_COOKIE_OPTIONS,
+        });
+      }
+    }
  }

88-116: LGTM with optional refactor.

The chunking and serialization logic correctly handles objects, primitives, and undefined values. Cookie naming and expiry settings are appropriate.

Consider the same iteration refactor as suggested for destroySession:

  async setSessionItem(
    itemKey: V | StorageKeys,
    itemValue: unknown,
  ): Promise<void> {
    const cookieStore = await this.ensureCookieStore();
-    cookieStore
-      .getAll()
-      .map((c) => c.name)
-      .forEach((key) => {
-        if (key.startsWith(`${String(itemKey)}`)) {
-          cookieStore.delete(key);
-        }
-      });
+    for (const { name } of cookieStore.getAll()) {
+      if (name.startsWith(`${String(itemKey)}`)) {
+        cookieStore.delete(name);
+      }
+    }
    if (itemValue !== undefined) {
      // ... rest remains the same
    }
  }

145-155: LGTM with optional refactor.

The removal logic correctly deletes all cookie chunks for the given key.

Apply the same iteration refactor as suggested for destroySession and setSessionItem:

  async removeSessionItem(itemKey: V | StorageKeys): Promise<void> {
    const cookieStore = await this.ensureCookieStore();
-    cookieStore
-      .getAll()
-      .map((c) => c.name)
-      .forEach((key) => {
-        if (key.startsWith(`${String(itemKey)}`)) {
-          cookieStore.delete(key);
-        }
-      });
+    for (const { name } of cookieStore.getAll()) {
+      if (name.startsWith(`${String(itemKey)}`)) {
+        cookieStore.delete(name);
+      }
+    }
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c809a17 and 6e1d234.

📒 Files selected for processing (4)
  • src/session/sessionManager/cookieManager.ts (1 hunks)
  • src/session/sessionManager/index.ts (1 hunks)
  • src/session/sessionManager/settings.ts (1 hunks)
  • tests/frontend/cookie-manager.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/session/sessionManager/settings.ts
  • src/session/sessionManager/index.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-13T10:45:31.961Z
Learnt from: DanielRivers
PR: kinde-oss/kinde-auth-nextjs#229
File: src/session/sessionManager.js:165-188
Timestamp: 2024-11-13T10:45:31.961Z
Learning: There's a new implementation coming soon for `src/session/sessionManager.js`, so refactoring suggestions related to cookie parsing logic may not be necessary at this time.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
🧬 Code graph analysis (2)
tests/frontend/cookie-manager.test.ts (3)
src/session/sessionManager.js (1)
  • options (27-27)
src/session/sessionManager/cookieManager.ts (1)
  • CookieStorage (23-156)
src/utils/constants.ts (2)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
src/session/sessionManager/cookieManager.ts (3)
src/session/sessionManager/settings.ts (1)
  • CookieStorageSettings (44-49)
src/utils/constants.ts (4)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
  • GLOBAL_COOKIE_OPTIONS (5-10)
  • TWENTY_NINE_DAYS (3-3)
src/session/sessionManager/types.ts (1)
  • SessionManager (61-105)
🔇 Additional comments (4)
tests/frontend/cookie-manager.test.ts (3)

1-5: LGTM!

Clean imports using the public API surface and test utilities.


6-36: LGTM!

The FakeCookieStore mock implementation accurately mirrors the cookie store API behavior, including correct handling of expired cookies via maxAge <= 0.


52-154: LGTM!

Comprehensive test coverage of core functionality:

  • Chunking and reassembly with correct naming conventions
  • Serialization/deserialization via destr
  • Removal of chunked cookies
  • Prefix-based destruction
  • Edge cases (undefined, primitives, objects)
src/session/sessionManager/cookieManager.ts (1)

47-63: Verify CookieManager constructor usage
Check all new CookieManager(...) instantiations to ensure req is only omitted (undefined) in App Router contexts so the cookies() fallback isn’t ever reached in Pages Router or middleware.

Comment thread src/session/sessionManager/cookieManager.ts
Comment thread src/session/sessionManager/cookieManager.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/session/sessionManager/cookieManager.ts (1)

123-145: Reconsider fallback to item.value on parse failure.

Line 143 falls back to item.value (first chunk only) when itemValue is falsy. If parsing fails but multiple chunks were successfully reassembled into an empty or falsy itemValue, returning only the first chunk loses data. Consider:

-      return itemValue || item.value;
+      return itemValue;

This ensures the full reassembled string is returned even when parsing fails, maintaining data integrity.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e95712 and fbe31d6.

📒 Files selected for processing (2)
  • src/session/sessionManager/cookieManager.ts (1 hunks)
  • tests/frontend/cookie-manager.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/frontend/cookie-manager.test.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-13T10:45:31.961Z
Learnt from: DanielRivers
PR: kinde-oss/kinde-auth-nextjs#229
File: src/session/sessionManager.js:165-188
Timestamp: 2024-11-13T10:45:31.961Z
Learning: There's a new implementation coming soon for `src/session/sessionManager.js`, so refactoring suggestions related to cookie parsing logic may not be necessary at this time.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
🧬 Code graph analysis (1)
src/session/sessionManager/cookieManager.ts (3)
src/session/sessionManager/settings.ts (1)
  • CookieStorageSettings (44-49)
src/utils/constants.ts (4)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
  • GLOBAL_COOKIE_OPTIONS (5-10)
  • TWENTY_NINE_DAYS (3-3)
src/session/sessionManager/types.ts (1)
  • SessionManager (61-105)
🔇 Additional comments (9)
src/session/sessionManager/cookieManager.ts (9)

1-15: LGTM!

All imports are appropriate for the cookie-based session storage implementation. The use of destr for safe JSON parsing aligns with best practices for handling untrusted data.


17-21: LGTM!

The storage settings are correctly configured with the unified "kinde-" prefix and appropriate security defaults.


23-26: LGTM!

Class declaration correctly extends SessionBase and implements SessionManager with appropriate generic constraints.


27-31: LGTM!

Class properties are correctly typed with appropriate visibility modifiers and sensible defaults.


33-42: LGTM!

Constructor signature correctly allows both persistent and session cookies, addressing previous feedback.


88-118: LGTM!

The method correctly:

  • Applies the unified prefix
  • Clears stale chunks before writing new ones
  • Handles serialization for both objects and primitives
  • Chunks large values appropriately
  • Respects the persistent flag for maxAge

150-161: LGTM!

The method correctly removes all chunked cookies associated with the given key by matching the prefix.


69-83: Use mutable RequestCookies for cookie mutations
Ensure ensureCookieStore returns RequestCookies (from next/headers) instead of ReadonlyRequestCookies, since set()/delete() require a mutable store.


47-63: Asymmetric App Router enforcement is correct. When req is undefined you’re already in App Router context; when provided, isAppRouter(req) accurately guards for web Request. No changes needed.

Comment thread src/session/sessionManager/types.ts Outdated
Comment thread src/session/sessionManager/settings.ts
Copy link
Copy Markdown
Contributor

@Yoshify Yoshify left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments - happy to discuss further 😄

* Clears all tracked items from the cookie store.
* @returns {void}
*/
async destroySession(): Promise<void> {
Copy link
Copy Markdown
Contributor

@Yoshify Yoshify Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unfortunately won't work in middleware in Next versions <15, it'll throw Error: Cookies can only be modified in a Server Action or Route Handler. - this applies to all mutations on cookies. We'd need to mutate the cookies on the req/res objects. See https://github.com/kinde-oss/kinde-auth-nextjs/blob/c8599cc4d0a8b6ef76e71ebac823dcc6dc526b10/src/authMiddleware/authMiddleware.ts

The reason we'd need to also mutate the cookies on the request and not just the response is during SSR in Next versions prior to 14.2.8, Cookies are read from the Set-Cookie from the request, not the response. See this part of the middleware specifically: https://github.com/kinde-oss/kinde-auth-nextjs/blob/c8599cc4d0a8b6ef76e71ebac823dcc6dc526b10/src/authMiddleware/authMiddleware.ts#L166C7-L170C39

It's a big gotcha that they resolved in 15 and above thankfully, but ideally we should continue supporting 14 as a good portion of the Next userbase is still on there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @Yoshify, I'd love a chance to discuss this with you further so I can better understand the desired flow within NextJS.

/**
* Sets the provided key-value store into cookies (split into chunks if needed).
*/
async setSessionItem(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above comment.

/**
* Removes all cookie parts associated with the provided key.
*/
async removeSessionItem(itemKey: V | StorageKeys): Promise<void> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above comment.

this._cookieStore = await cookies();
return this._cookieStore;
} else {
throw new Error("This store is to be used for App Router.");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of throwing here we should probably use the request cookies to support middleware in v14> <v15, that will also potentially allow us to use the same store for pages router. This shouldn't be too difficult given we have the req/res objects already. https://github.com/kinde-oss/kinde-auth-nextjs/blob/c8599cc4d0a8b6ef76e71ebac823dcc6dc526b10/src/authMiddleware/authMiddleware.ts has some good examples of this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Yoshify I've made some changes to the way this works. Can you re-review please?

Comment thread src/session/sessionManager/cookieManager.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/session/sessionManager/cookieManager.ts (3)

110-176: Consider extracting the serialization logic

The cookie serialization and chunking logic is duplicated between the middleware and non-middleware branches (lines 132-150 and 158-174). While the current implementation is correct, extracting this into a helper function would improve maintainability.

Example refactor:

private prepareChunks(itemValue: unknown): string[] {
  if (itemValue === undefined) return [];
  const itemValueString =
    typeof itemValue === "object"
      ? JSON.stringify(itemValue)
      : String(itemValue);
  return splitString(itemValueString, MAX_COOKIE_LENGTH);
}

Then use it in both branches:

const chunks = this.prepareChunks(itemValue);
chunks.forEach((value, index) => {
  // set cookie with appropriate API
});

232-236: Consider parallelizing batch operations

The method sequentially awaits each setSessionItem call. If ordering is not critical, you could improve performance by parallelizing with Promise.all.

async setItems(items: Partial<Record<V, unknown>>): Promise<void> {
  await Promise.all(
    Object.entries(items).map(([key, value]) =>
      this.setSessionItem(key as V, value)
    )
  );
}

241-245: Consider parallelizing batch operations

Similar to setItems, this method could benefit from parallelization if ordering is not critical.

async removeItems(...items: V[]): Promise<void> {
  await Promise.all(items.map((item) => this.removeSessionItem(item)));
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fbe31d6 and 4d23613.

⛔ Files ignored due to path filters (2)
  • package.json is excluded by !**/*.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/*.yaml
📒 Files selected for processing (3)
  • src/session/sessionManager/cookieManager.ts (1 hunks)
  • src/session/sessionManager/index.ts (1 hunks)
  • src/session/sessionManager/settings.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/session/sessionManager/settings.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-13T10:45:31.961Z
Learnt from: DanielRivers
PR: kinde-oss/kinde-auth-nextjs#229
File: src/session/sessionManager.js:165-188
Timestamp: 2024-11-13T10:45:31.961Z
Learning: There's a new implementation coming soon for `src/session/sessionManager.js`, so refactoring suggestions related to cookie parsing logic may not be necessary at this time.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
📚 Learning: 2024-12-17T00:41:07.608Z
Learnt from: Yoshify
PR: kinde-oss/kinde-auth-nextjs#254
File: src/session/isAuthenticated.js:14-17
Timestamp: 2024-12-17T00:41:07.608Z
Learning: In `src/session/isAuthenticated.js` of this Next.js application, cookies cannot be modified in React Server Components (RSC). Therefore, to prevent accessing stale data outside of middleware, the application redirects on token expiry.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
🧬 Code graph analysis (1)
src/session/sessionManager/cookieManager.ts (3)
src/session/sessionManager/settings.ts (1)
  • CookieStorageSettings (3-8)
src/utils/constants.ts (4)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
  • GLOBAL_COOKIE_OPTIONS (5-10)
  • TWENTY_NINE_DAYS (3-3)
src/session/sessionManager.js (1)
  • options (27-27)
🔇 Additional comments (8)
src/session/sessionManager/index.ts (1)

1-4: LGTM!

The re-exports are clean and follow proper TypeScript conventions. The previous issue with file extensions has been correctly addressed.

src/session/sessionManager/cookieManager.ts (7)

1-15: LGTM!

The imports correctly use Next.js App Router types (NextRequest, NextResponse) rather than Pages Router types, addressing previous feedback.


17-21: LGTM!

The settings are well-defined, and the keyPrefix is consistently applied across all cookie operations in the class methods.


31-42: LGTM!

The constructor correctly accepts both persistent and session-scoped cookie configurations, addressing the previous type constraint issue.


48-50: LGTM!

Simple and correct helper for detecting middleware context.


79-105: LGTM!

The method correctly differentiates between middleware and non-middleware contexts for cookie deletion. The middleware path properly uses resp.cookies for mutations to support Next.js < 15.


181-203: LGTM!

The method correctly reassembles chunked cookies with proper null-checking to prevent race conditions, and safely parses values using destr.


208-227: LGTM!

The method correctly removes all cookie chunks with proper context differentiation.

Comment thread src/session/sessionManager/cookieManager.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/session/sessionManager/cookieManager.ts (2)

232-236: Consider parallel execution for better performance.

The sequential await calls may be slower when setting multiple items. While the current implementation is correct and easier to reason about, parallel execution could improve performance.

If you'd like to optimize, apply this diff:

  async setItems(items: Partial<Record<V, unknown>>): Promise<void> {
-   for (const [key, value] of Object.entries(items)) {
-     await this.setSessionItem(key as V, value);
-   }
+   await Promise.all(
+     Object.entries(items).map(([key, value]) =>
+       this.setSessionItem(key as V, value)
+     )
+   );
  }

Note: Only apply this optimization if profiling shows it's beneficial, as sequential execution may be preferred for debugging or when order matters.


241-245: Consider parallel execution for better performance.

Similar to setItems, the sequential await calls may be slower when removing multiple items.

If you'd like to optimize:

  async removeItems(...items: V[]): Promise<void> {
-   for (const item of items) {
-     await this.removeSessionItem(item);
-   }
+   await Promise.all(items.map((item) => this.removeSessionItem(item)));
  }

Note: Only apply if profiling shows benefit, as sequential execution may be preferred for debugging or when order matters.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d23613 and 6499c16.

⛔ Files ignored due to path filters (2)
  • package.json is excluded by !**/*.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/*.yaml
📒 Files selected for processing (1)
  • src/session/sessionManager/cookieManager.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-13T10:45:31.961Z
Learnt from: DanielRivers
PR: kinde-oss/kinde-auth-nextjs#229
File: src/session/sessionManager.js:165-188
Timestamp: 2024-11-13T10:45:31.961Z
Learning: There's a new implementation coming soon for `src/session/sessionManager.js`, so refactoring suggestions related to cookie parsing logic may not be necessary at this time.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
📚 Learning: 2024-12-17T00:41:07.608Z
Learnt from: Yoshify
PR: kinde-oss/kinde-auth-nextjs#254
File: src/session/isAuthenticated.js:14-17
Timestamp: 2024-12-17T00:41:07.608Z
Learning: In `src/session/isAuthenticated.js` of this Next.js application, cookies cannot be modified in React Server Components (RSC). Therefore, to prevent accessing stale data outside of middleware, the application redirects on token expiry.

Applied to files:

  • src/session/sessionManager/cookieManager.ts
🧬 Code graph analysis (1)
src/session/sessionManager/cookieManager.ts (3)
src/session/sessionManager/settings.ts (1)
  • CookieStorageSettings (3-8)
src/utils/constants.ts (4)
  • MAX_COOKIE_LENGTH (23-23)
  • COOKIE_LIST (12-21)
  • GLOBAL_COOKIE_OPTIONS (5-10)
  • TWENTY_NINE_DAYS (3-3)
src/session/sessionManager.js (1)
  • options (27-27)
🔇 Additional comments (8)
src/session/sessionManager/cookieManager.ts (8)

17-21: LGTM!

The cookie storage settings are properly configured with sensible defaults. The keyPrefix alignment and maxLength constraint prevent cookie size issues.


23-42: LGTM!

The class declaration and constructor are well-structured. The constructor properly accepts both persistent modes, resolving the previous type constraint issue.


44-50: LGTM!

The middleware context detection is correct and well-documented. The comment clearly explains the Next.js < 15 requirement.


75-105: LGTM!

The session destruction logic correctly handles both middleware and server contexts. The filtering by COOKIE_LIST prefixes and setting maxAge: 0 is the proper approach for cookie deletion.


110-176: LGTM with a minor note on serialization.

The chunking logic and dual-path handling for middleware/server contexts are well-implemented. The keyPrefix is now correctly applied to all operations.

One minor consideration: Line 135 uses String(itemValue) for non-object primitives. This works for numbers and booleans but converts null to "null" string. Ensure upstream code doesn't pass null when undefined is intended, or add explicit null handling if needed.


181-203: LGTM!

The reassembly logic with null guard (line 191-192) properly handles edge cases. The use of destr for safe parsing and the debug logging for parse failures follow best practices.

Based on learnings.


208-227: LGTM!

The removal logic correctly handles all chunks and maintains consistency with the dual-path approach used throughout the class.


55-73: Add middleware context branch for Next.js < 14.2.8
Current code always calls await cookies() in middleware, so mutations via resp.cookies aren’t visible in Next.js < 14.2.8. Introduce before the existing branches:

 private async ensureCookieStore(): Promise<ReadonlyRequestCookies> {
   if (this._cookieStore) return this._cookieStore;

+  if (this.isMiddlewareContext() && this.req) {
+    this._cookieStore = this.req.cookies;
+    return this._cookieStore;
+  }

   if (!this.req) {
     this._cookieStore = await cookies();

Confirm that NextRequest.cookies is compatible with ReadonlyRequestCookies.

Comment thread src/session/sessionManager/cookieManager.ts Outdated
@pesickaa pesickaa requested a review from a team as a code owner October 14, 2025 09:04
@pesickaa pesickaa requested a review from a team as a code owner February 2, 2026 11:21
Copy link
Copy Markdown
Contributor

@dtoxvanilla1991 dtoxvanilla1991 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! Some feedback 💭

.map((c) => c.name)
.filter((key) => COOKIE_LIST.some((substr) => key.startsWith(substr)));

// In middleware context, use resp.cookies (Next.js < 15 requirement)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

destroySession() currently filters only by COOKIE_LIST prefixes, but this class writes session keys using kinde- prefixed names. That mismatch can leave prefixed auth cookies behind after logout/session destruction. Please align delete matching with the same namespace strategy used by setSessionItem/getSessionItem/removeSessionItem (and preserve legacy cleanup if needed).

Comment on lines +69 to +74
this._cookieStore = cookies();
return this._cookieStore;
}

if (isAppRouter(this.req)) {
this._cookieStore = cookies();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cookies() in current Next.js versions is async and returns a promise-backed cookie store (Promise<ReadonlyRequestCookies>). Assigning it directly here risks runtime/type drift in non-middleware paths, and subsequent .set/.delete calls can fail depending on context. Please resolve this branch to await and normalize store type consistently with the rest of ensureCookieStore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtoxvanilla1991 Can you please verify this is done to your satisfaction?

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/session/sessionManager/cookieManager.ts (1)

10-14: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix Next.js 12 compatibility: unguarded next/headers imports
src/session/sessionManager/cookieManager.ts imports cookies from next/headers.js (and App Router types) at module scope. next/headers was introduced in Next 13, so Next 12.3.5 installs (allowed by this package’s peerDependencies.next) will fail when bundling. Also, src/session/sessionManager.js has a top-level import { cookies } from "next/headers";, so the incompatibility isn’t limited to cookieManager.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/session/sessionManager/cookieManager.ts` around lines 10 - 14, The module
imports the runtime symbol cookies from "next/headers" at top-level (in
cookieManager.ts and sessionManager.js), which breaks on Next 12; move the
runtime import into the functions that actually call cookies (i.e., replace the
top-level import of the cookies symbol with a dynamic/delayed require or import
inside the function that uses cookies), wrap that import in a try/catch to
provide a clear fallback/error for Next 12 users, and remove or convert any
non-runtime-only imports to type-only imports if necessary; specifically,
eliminate the top-level `import { cookies } from "next/headers"` and instead
perform a guarded dynamic import within the functions in cookieManager.ts (and
the analogous location in sessionManager.js) so bundlers on Next 12 won’t
attempt to resolve next/headers at module-evaluation time.
♻️ Duplicate comments (1)
src/session/sessionManager/cookieManager.ts (1)

82-89: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mirror middleware mutations into the request cookie store too.

ensureCookieStore() caches this.req.cookies, but the middleware branches only mutate this.resp.cookies. After the first set/delete, later reads and cleanup in the same request still operate on stale request data, so getSessionItem()/destroySession() can miss cookies written earlier in that request. Update req.cookies alongside resp.cookies, or replace _cookieStore with a mutable shadow store after the first mutation.

#!/bin/bash
set -euo pipefail

echo "Current CookieStorage middleware branches:"
sed -n '82,235p' src/session/sessionManager/cookieManager.ts

echo
echo "Existing middleware cookie-sync patterns in the repo:"
fd -p 'authMiddleware.ts' src | while read -r file; do
  echo "FILE: $file"
  rg -n "copyCookiesToRequest|req\.cookies\.(set|delete)|resp\.cookies\.(set|delete)" "$file" -C2
done

Also applies to: 117-135, 162-185, 219-234

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/session/sessionManager/cookieManager.ts` around lines 82 - 89,
ensureCookieStore() currently caches this.req.cookies but middleware branches
only mutate this.resp.cookies, leaving this._cookieStore stale and causing
getSessionItem()/destroySession() to miss in-request changes; update the
middleware branches in cookieManager.ts (methods using isMiddlewareContext(),
ensureCookieStore(), and any code that calls resp.cookies.set/delete) to mirror
mutations into this.req.cookies as they occur or, after the first mutation,
replace this._cookieStore with a mutable shadow store and route subsequent
reads/writes to that shadow so both reads (getSessionItem) and cleanup
(destroySession) see up-to-date cookie state. Ensure changes touch the same
unique symbols: this._cookieStore, this.req.cookies, this.resp.cookies,
ensureCookieStore(), isMiddlewareContext(), getSessionItem(), and
destroySession().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/session/sessionManager/cookieManager.ts`:
- Around line 10-14: The module imports the runtime symbol cookies from
"next/headers" at top-level (in cookieManager.ts and sessionManager.js), which
breaks on Next 12; move the runtime import into the functions that actually call
cookies (i.e., replace the top-level import of the cookies symbol with a
dynamic/delayed require or import inside the function that uses cookies), wrap
that import in a try/catch to provide a clear fallback/error for Next 12 users,
and remove or convert any non-runtime-only imports to type-only imports if
necessary; specifically, eliminate the top-level `import { cookies } from
"next/headers"` and instead perform a guarded dynamic import within the
functions in cookieManager.ts (and the analogous location in sessionManager.js)
so bundlers on Next 12 won’t attempt to resolve next/headers at
module-evaluation time.

---

Duplicate comments:
In `@src/session/sessionManager/cookieManager.ts`:
- Around line 82-89: ensureCookieStore() currently caches this.req.cookies but
middleware branches only mutate this.resp.cookies, leaving this._cookieStore
stale and causing getSessionItem()/destroySession() to miss in-request changes;
update the middleware branches in cookieManager.ts (methods using
isMiddlewareContext(), ensureCookieStore(), and any code that calls
resp.cookies.set/delete) to mirror mutations into this.req.cookies as they occur
or, after the first mutation, replace this._cookieStore with a mutable shadow
store and route subsequent reads/writes to that shadow so both reads
(getSessionItem) and cleanup (destroySession) see up-to-date cookie state.
Ensure changes touch the same unique symbols: this._cookieStore,
this.req.cookies, this.resp.cookies, ensureCookieStore(), isMiddlewareContext(),
getSessionItem(), and destroySession().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 468b1009-cf5b-47ba-8ade-823da8f0909d

📥 Commits

Reviewing files that changed from the base of the PR and between 52786a3 and af47853.

⛔ Files ignored due to path filters (2)
  • package.json is excluded by !**/*.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/*.yaml
📒 Files selected for processing (2)
  • src/session/sessionManager/cookieManager.ts
  • tests/frontend/cookie-manager.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/frontend/cookie-manager.test.ts

@pesickaa
Copy link
Copy Markdown
Contributor Author

pesickaa commented Jun 3, 2026

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)

src/session/sessionManager/cookieManager.ts (1)> 10-14: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix Next.js 12 compatibility: unguarded next/headers imports
src/session/sessionManager/cookieManager.ts imports cookies from next/headers.js (and App Router types) at module scope. next/headers was introduced in Next 13, so Next 12.3.5 installs (allowed by this package’s peerDependencies.next) will fail when bundling. Also, src/session/sessionManager.js has a top-level import { cookies } from "next/headers";, so the incompatibility isn’t limited to cookieManager.ts.

🤖 Prompt for AI Agents

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/session/sessionManager/cookieManager.ts` around lines 10 - 14, The module
imports the runtime symbol cookies from "next/headers" at top-level (in
cookieManager.ts and sessionManager.js), which breaks on Next 12; move the
runtime import into the functions that actually call cookies (i.e., replace the
top-level import of the cookies symbol with a dynamic/delayed require or import
inside the function that uses cookies), wrap that import in a try/catch to
provide a clear fallback/error for Next 12 users, and remove or convert any
non-runtime-only imports to type-only imports if necessary; specifically,
eliminate the top-level `import { cookies } from "next/headers"` and instead
perform a guarded dynamic import within the functions in cookieManager.ts (and
the analogous location in sessionManager.js) so bundlers on Next 12 won’t
attempt to resolve next/headers at module-evaluation time.

♻️ Duplicate comments (1)

🤖 Prompt for all review comments with AI agents

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/session/sessionManager/cookieManager.ts`:
- Around line 10-14: The module imports the runtime symbol cookies from
"next/headers" at top-level (in cookieManager.ts and sessionManager.js), which
breaks on Next 12; move the runtime import into the functions that actually call
cookies (i.e., replace the top-level import of the cookies symbol with a
dynamic/delayed require or import inside the function that uses cookies), wrap
that import in a try/catch to provide a clear fallback/error for Next 12 users,
and remove or convert any non-runtime-only imports to type-only imports if
necessary; specifically, eliminate the top-level `import { cookies } from
"next/headers"` and instead perform a guarded dynamic import within the
functions in cookieManager.ts (and the analogous location in sessionManager.js)
so bundlers on Next 12 won’t attempt to resolve next/headers at
module-evaluation time.

---

Duplicate comments:
In `@src/session/sessionManager/cookieManager.ts`:
- Around line 82-89: ensureCookieStore() currently caches this.req.cookies but
middleware branches only mutate this.resp.cookies, leaving this._cookieStore
stale and causing getSessionItem()/destroySession() to miss in-request changes;
update the middleware branches in cookieManager.ts (methods using
isMiddlewareContext(), ensureCookieStore(), and any code that calls
resp.cookies.set/delete) to mirror mutations into this.req.cookies as they occur
or, after the first mutation, replace this._cookieStore with a mutable shadow
store and route subsequent reads/writes to that shadow so both reads
(getSessionItem) and cleanup (destroySession) see up-to-date cookie state.
Ensure changes touch the same unique symbols: this._cookieStore,
this.req.cookies, this.resp.cookies, ensureCookieStore(), isMiddlewareContext(),
getSessionItem(), and destroySession().

ℹ️ Review info

@DanielRivers @Yoshify Should I implement this nitpick? Or is it not necessary?

@pesickaa pesickaa requested a review from dtoxvanilla1991 June 3, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants