[GH-ISSUE #6372] Next.js App Router: Memory adapter causes "Account not found" in Server Components #19129

Closed
opened 2026-04-15 17:55:27 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @kenfdev on GitHub (Nov 28, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6372

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Set up a Next.js 14+ App Router project with better-auth using stateless authentication:
// lib/auth.ts
import { betterAuth } from 'better-auth';

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 7 * 24 * 60 * 60, // 7 days cache duration
      strategy: "jwe", // can be "jwt" or "compact"
      refreshCache: true, // Enable stateless refresh
    },
  },
  account: {
    updateAccountOnSignIn:true,
    // @ts-ignore
    storeStateStrategy: "cookie",
    storeAccountCookie: true, // Store account data after OAuth flow in a cookie (useful for database-less flows)
  },
  // This plugin is essential if you want to use server actions.
  // see: https://www.better-auth.com/docs/integrations/next#server-action-cookies
  plugins: [nextCookies()],
});
  1. Create an API route handler:
// app/api/auth/[...all]/route.ts
import { auth } from '@/lib/auth';
import { toNextJsHandler } from 'better-auth/next-js';

export const { GET, POST } = toNextJsHandler(auth);
  1. Sign in via OAuth (e.g., GitHub) through the API endpoint - this works and stores account data in the memory adapter

  2. Try to access the account from a Server Component:

// app/dashboard/page.tsx
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';

export default async function Dashboard() {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  // Try to get OAuth access token
  const { idToken } = await auth.api.getAccessToken({
    body: {
      providerId: "github",
    },
    headers: await headers(),
  });

  return <div>{idToken}</div>;
}
  1. Observe the error: Error: Account not found

Current vs. Expected behavior

Current behavior:

When using the stateless authentication with Next.js App Router, Server Components and API Routes maintain separate instances of the in-memory database - IIUC. This happens because Next.js
bundles these contexts separately, causing the same module to be evaluated twice with different memory spaces.

Result:

  • OAuth sign-in via API route stores account data in its memory adapter instance
  • Server Components read from a different memory adapter instance (empty)
  • auth.api.getAccessToken() throws "Account not found" error despite valid session cookies

Expected behavior:

Developers should not care about memory adapters when using stateless authentication.

One solution I can think about is having a single memory adapter instance shared across all Next.js execution contexts (API routes, Server Components, middleware, etc.) but am not sure about the side-effects at the moment.

What version of Better Auth are you using?

1.4.4-beta.1

System info

{
  "system": {
    "platform": "linux",
    "arch": "x64",
    "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025",
    "release": "6.6.87.2-microsoft-standard-WSL2",
    "cpuCount": 24,
    "cpuModel": "13th Gen Intel(R) Core(TM) i7-13700F",
    "totalMemory": "47.04 GB",
    "freeMemory": "35.74 GB"
  },
  "node": {
    "version": "v20.19.6",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.8.2"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend, Client

Auth config (if applicable)

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 7 * 24 * 60 * 60, // 7 days cache duration
      strategy: "jwe", // can be "jwt" or "compact"
      refreshCache: true, // Enable stateless refresh
    },
  },
  account: {
    updateAccountOnSignIn:true,
    // @ts-ignore
    storeStateStrategy: "cookie",
    storeAccountCookie: true, // Store account data after OAuth flow in a cookie (useful for database-less flows)
  },
  // This plugin is essential if you want to use server actions.
  // see: https://www.better-auth.com/docs/integrations/next#server-action-cookies
  plugins: [nextCookies()],
});

Additional context

Workaround (not robust, not sure if it's safe either):

Using globalThis to share the memory adapter instance across Next.js contexts:

const globalForDb = globalThis as unknown as {
  memoryDb: Record<string, any[]> | undefined;
};

if (!globalForDb.memoryDb) {
  globalForDb.memoryDb = {
    user: [],
    session: [],
    account: [],
    verification: [],
  };
}

export const auth = betterAuth({
  database: memoryAdapter(globalForDb.memoryDb),
  // ... rest of config
});
Originally created by @kenfdev on GitHub (Nov 28, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6372 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Set up a Next.js 14+ App Router project with better-auth using stateless authentication: ```ts // lib/auth.ts import { betterAuth } from 'better-auth'; export const auth = betterAuth({ socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }, }, session: { cookieCache: { enabled: true, maxAge: 7 * 24 * 60 * 60, // 7 days cache duration strategy: "jwe", // can be "jwt" or "compact" refreshCache: true, // Enable stateless refresh }, }, account: { updateAccountOnSignIn:true, // @ts-ignore storeStateStrategy: "cookie", storeAccountCookie: true, // Store account data after OAuth flow in a cookie (useful for database-less flows) }, // This plugin is essential if you want to use server actions. // see: https://www.better-auth.com/docs/integrations/next#server-action-cookies plugins: [nextCookies()], }); ``` 2. Create an API route handler: ```ts // app/api/auth/[...all]/route.ts import { auth } from '@/lib/auth'; import { toNextJsHandler } from 'better-auth/next-js'; export const { GET, POST } = toNextJsHandler(auth); ``` 3. Sign in via OAuth (e.g., GitHub) through the API endpoint - this works and stores account data in the memory adapter 4. Try to access the account from a Server Component: ```tsx // app/dashboard/page.tsx import { auth } from '@/lib/auth'; import { headers } from 'next/headers'; export default async function Dashboard() { const session = await auth.api.getSession({ headers: await headers(), }); // Try to get OAuth access token const { idToken } = await auth.api.getAccessToken({ body: { providerId: "github", }, headers: await headers(), }); return <div>{idToken}</div>; } ``` 5. Observe the error: Error: Account not found ### Current vs. Expected behavior ## Current behavior: When using the stateless authentication with Next.js App Router, Server Components and API Routes maintain separate instances of the in-memory database - IIUC. This happens because Next.js bundles these contexts separately, causing the same module to be evaluated twice with different memory spaces. Result: - OAuth sign-in via API route stores account data in its memory adapter instance - Server Components read from a different memory adapter instance (empty) - auth.api.getAccessToken() throws "Account not found" error despite valid session cookies ## Expected behavior: Developers should not care about memory adapters when using stateless authentication. One solution I can think about is having a single memory adapter instance shared across all Next.js execution contexts (API routes, Server Components, middleware, etc.) but am not sure about the side-effects at the moment. ### What version of Better Auth are you using? 1.4.4-beta.1 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025", "release": "6.6.87.2-microsoft-standard-WSL2", "cpuCount": 24, "cpuModel": "13th Gen Intel(R) Core(TM) i7-13700F", "totalMemory": "47.04 GB", "freeMemory": "35.74 GB" }, "node": { "version": "v20.19.6", "env": "development" }, "packageManager": { "name": "npm", "version": "10.8.2" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript export const auth = betterAuth({ socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }, }, session: { cookieCache: { enabled: true, maxAge: 7 * 24 * 60 * 60, // 7 days cache duration strategy: "jwe", // can be "jwt" or "compact" refreshCache: true, // Enable stateless refresh }, }, account: { updateAccountOnSignIn:true, // @ts-ignore storeStateStrategy: "cookie", storeAccountCookie: true, // Store account data after OAuth flow in a cookie (useful for database-less flows) }, // This plugin is essential if you want to use server actions. // see: https://www.better-auth.com/docs/integrations/next#server-action-cookies plugins: [nextCookies()], }); ``` ### Additional context Workaround (not robust, not sure if it's safe either): Using globalThis to share the memory adapter instance across Next.js contexts: ```ts const globalForDb = globalThis as unknown as { memoryDb: Record<string, any[]> | undefined; }; if (!globalForDb.memoryDb) { globalForDb.memoryDb = { user: [], session: [], account: [], verification: [], }; } export const auth = betterAuth({ database: memoryAdapter(globalForDb.memoryDb), // ... rest of config }); ```
GiteaMirror added the locked label 2026-04-15 17:55:27 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 28, 2025):

Hi there, thanks for raising this issue! It appears that the problem stems from Next.js’s module isolation when running under the App Router—API routes and Server Components are getting isolated instances of your in-memory adapter. This causes the account data, which is stored by one instance during the OAuth sign‑in process, not to be accessible in the separate instance that your Server Component is reading from, hence the “Account not found” error.

A recommended troubleshooting step is to share a single memory adapter instance across your Next.js contexts. One approach, as you noted, is to attach your memory adapter to globalThis, ensuring that all parts of your app reference the same instance. However, please note that while this workaround works in development, using a persistent storage adapter is generally a safer option in production for stateless authentication.

Are you considering switching to a database-backed adapter, or does this workaround with globalThis fit your current needs? tag @better-auth-agent if you would like an answer from the Agent.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3587977005 --> @better-auth-agent[bot] commented on GitHub (Nov 28, 2025): Hi there, thanks for raising this issue! It appears that the problem stems from Next.js’s module isolation when running under the App Router—API routes and Server Components are getting isolated instances of your in-memory adapter. This causes the account data, which is stored by one instance during the OAuth sign‑in process, not to be accessible in the separate instance that your Server Component is reading from, hence the “Account not found” error. A recommended troubleshooting step is to share a single memory adapter instance across your Next.js contexts. One approach, as you noted, is to attach your memory adapter to globalThis, ensuring that all parts of your app reference the same instance. However, please note that while this workaround works in development, using a persistent storage adapter is generally a safer option in production for stateless authentication. Are you considering switching to a database-backed adapter, or does this workaround with globalThis fit your current needs? tag @better-auth-agent if you would like an answer from the Agent. <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@jslno commented on GitHub (Nov 28, 2025):

Can't reproduce. You've set cookieCache.strategy to jwe instead of jwt. Fixing the typo should solve the issue.

<!-- gh-comment-id:3588080475 --> @jslno commented on GitHub (Nov 28, 2025): Can't reproduce. You've set cookieCache.strategy to `jwe` instead of `jwt`. Fixing the typo should solve the issue.
Author
Owner

@kenfdev commented on GitHub (Nov 28, 2025):

@jslno
Thanks. jwe is intended. (JSON Web Encryption)

https://www.better-auth.com/docs/concepts/session-management#cookie-cache-strategies

Or is that not what you meant? (Do you mean jwe is not supported?)

Can't reproduce

Does that mean your Next.js app works as expected?

Thanks in advance.

<!-- gh-comment-id:3588091030 --> @kenfdev commented on GitHub (Nov 28, 2025): @jslno Thanks. `jwe` is intended. (JSON Web Encryption) https://www.better-auth.com/docs/concepts/session-management#cookie-cache-strategies Or is that not what you meant? (Do you mean `jwe` is not supported?) > Can't reproduce Does that mean your Next.js app works as expected? Thanks in advance.
Author
Owner

@jslno commented on GitHub (Nov 28, 2025):

You're right, its supported (my bad wasn't aware). I couldn't reproduce because I was testing with Google provider. This only seems to occur when using Github provider with jwe specifically. I'll look into it

<!-- gh-comment-id:3588123608 --> @jslno commented on GitHub (Nov 28, 2025): You're right, its supported (my bad wasn't aware). I couldn't reproduce because I was testing with Google provider. This only seems to occur when using Github provider with jwe specifically. I'll look into it
Author
Owner

@kenfdev commented on GitHub (Nov 28, 2025):

@jslno
Thanks again. I'll try and create a minimum reproducable app.

This only seems to occur when using Github provider

To be sure, when you authenticate via Google and come back to the app, your Server Component (not client component) returns the Access Token (via getAccessToken in the server component. not via the API endpoint)? I'm asking because this didn't work in a Google provider setup either 🤔

<!-- gh-comment-id:3588136780 --> @kenfdev commented on GitHub (Nov 28, 2025): @jslno Thanks again. I'll try and create a minimum reproducable app. > This only seems to occur when using Github provider To be sure, when you authenticate via Google and come back to the app, your Server Component (not client component) returns the Access Token (via `getAccessToken` in the server component. not via the API endpoint)? I'm asking because this didn't work in a Google provider setup either 🤔
Author
Owner

@jslno commented on GitHub (Nov 28, 2025):

Yes, copy & pasting your code and only replacing github with google works perfectly fine for me

<!-- gh-comment-id:3588146552 --> @jslno commented on GitHub (Nov 28, 2025): Yes, copy & pasting your code and only replacing github with google works perfectly fine for me
Author
Owner

@kenfdev commented on GitHub (Nov 28, 2025):

Hmm... interesting.So that probably means something environment specific is happening on my side. I'll dig a bit further by starting everything from scratch. Thanks a lot.

<!-- gh-comment-id:3588160453 --> @kenfdev commented on GitHub (Nov 28, 2025): Hmm... interesting.So that probably means something environment specific is happening on my side. I'll dig a bit further by starting everything from scratch. Thanks a lot.
Author
Owner

@kenfdev commented on GitHub (Nov 28, 2025):

@jslno
I probably found the issue and need to apologize. This seems to be a cognito specific issue. I thought reproducing with github would be easier so I've set the description to be github specific but that was false information. (it didn't work in my local environment but that was probably because I used an older version of better-auth)

That said, I think this problem occurs because better-auth is trying to set the account_data cookie with the huge data returned by `cognito' (and likely fails to set it).

Should I create a separate issue and close this one? Or should I edit the descriptions to explain the actual problem.

Again, apologies about the confusion. My minimum reproduction did actually work with GitHub, Google. And it fails with Cognito.

<!-- gh-comment-id:3588490258 --> @kenfdev commented on GitHub (Nov 28, 2025): @jslno I probably found the issue and need to apologize. This seems to be a cognito specific issue. I thought reproducing with `github` would be easier so I've set the description to be github specific but that was false information. (it didn't work in my local environment but that was probably because I used an older version of better-auth) That said, I think this problem occurs because better-auth is trying to set the `account_data` cookie with the huge data returned by `cognito' (and likely fails to set it). Should I create a separate issue and close this one? Or should I edit the descriptions to explain the actual problem. Again, apologies about the confusion. My minimum reproduction did actually work with GitHub, Google. And it fails with Cognito.
Author
Owner

@kenfdev commented on GitHub (Nov 28, 2025):

I'm closing this issue because the description was misleading and didn't capture the core problem.

I've opened a new issue with more accurate details here: https://github.com/better-auth/better-auth/issues/6379

Sorry for the confusion.

<!-- gh-comment-id:3589122885 --> @kenfdev commented on GitHub (Nov 28, 2025): I'm closing this issue because the description was misleading and didn't capture the core problem. I've opened a new issue with more accurate details here: https://github.com/better-auth/better-auth/issues/6379 Sorry for the confusion.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19129