[GH-ISSUE #7994] [Bug] updateAge does not extend session_token cookie expiry in stateless mode #19598

Closed
opened 2026-04-15 18:52:27 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @Rohit-sharma04 on GitHub (Feb 16, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7994

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Setup:
Nextjs 16 (App router)
Better Auth stateless configuration

session config:

expiresIn = 5 mins,
updateAge = 2 mins
cookieCache.maxAge = 5 mins
cookieCache.refreshcache = true

Current vs. Expected behavior

Current Behavior
After login:

Image

After 2 mins (updateAge) of login:
Whether I hit getsession from client or middleware (auth.api.getSession) , the session_token cookie expiry does not change.

Image

After 4 mins (refreshCache) of login:
When i hit getSession from client or middleware, it updates the expiry of session_data and account_data but session_token cookie remains unchanged.

Image

Result:
The user is logged out at exactly 5 min after login, because the main token never rotates or extends.

Expected:
After 2 mins (updateAge) of login:
The session_token expiry should update after calling getSession to extend the session lifetime, preventing forced logout when the user is active.

What version of Better Auth are you using?

1.4.18

System info

- better-auth: 1.4.18
- Node.js: v22.22.0
- npm: 11.7.0
- OS: Windows
- Framework: Next.js 16.1.6
- React: 19.2.3
- TypeScript: 5.x

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

Client, Backend, Other

Auth config (if applicable)

export const auth = betterAuth({
  socialProviders: {
    microsoft: {
      clientId: process.env.APP_CLIENT_ID ?? "",
      clientSecret: process.env.APP_CLIENT_SECRET ?? "",
      tenantId: process.env.TENANT_ID ?? "",
      scope: MICROSOFT_AUTH_SCOPES,
    },
  },

  session: {
    expiresIn: 60 * 5, // 5 min
    updateAge: 60 * 2, // 2 min
    cookieCache: {
      enabled: true,
      maxAge: 60 * 5, // 5 min
      strategy: "jwe",
      refreshCache: true, // refreshes session data cookies after 4 min
    },
  },
  account: {
    storeStateStrategy: "cookie", // Default
    storeAccountCookie: true, // Default for stateless
  },
  plugins: [nextCookies()],
});

Additional context

proxy.ts-

export async function proxy(request: NextRequest) {
  const sessionResponse = await auth.api.getSession({
    headers: await headers(),
    asResponse: true,
    returnHeaders: true,
  });

  const session = await sessionResponse.json();

  if (!session) {
    return NextResponse.redirect(new URL("/sign-in", request.url));
  }

  const sessionHeader = sessionResponse.headers.get("set-cookie");
  if (sessionHeader) {
    const response = NextResponse.next();
    response.headers.set("set-cookie", sessionHeader);
    return response;
  }

  return NextResponse.next();
}

Client test page
page.tsx

"use client";

import { createAuthClient } from "better-auth/react";

const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000",
});

export default function Page() {
  const handleGetSession = async () => {
    const { data, error } = await authClient.getSession();
    if (error) {
      console.error("Failed to fetch session", error);
      return;
    }
    console.log("Session", data);
  };

  return (
    <div className="center flex flex-col gap-2 p-4">
      <div>Home Page</div>
      <button
        className={`
          border-gray-400 w-32 rounded border px-3 py-1 text-sm
          font-medium
        `}
        onClick={handleGetSession}
      >
        Get Session
      </button>
    </div>
  );
}
Originally created by @Rohit-sharma04 on GitHub (Feb 16, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7994 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce **Setup:** Nextjs 16 (App router) Better Auth stateless configuration **session config:** ``` expiresIn = 5 mins, updateAge = 2 mins cookieCache.maxAge = 5 mins cookieCache.refreshcache = true ``` ### Current vs. Expected behavior **Current Behavior** After login: <img width="625" height="154" alt="Image" src="https://github.com/user-attachments/assets/d90d0de8-a9dd-4e01-866c-a038342257e0" /> **After 2 mins (updateAge) of login:** Whether I hit **_getsession_** from client or middleware (auth.api.getSession) , the **_session_token_** cookie expiry does not change. <img width="630" height="142" alt="Image" src="https://github.com/user-attachments/assets/1c64dbec-a560-4a83-9112-fbbc704bc8c2" /> **After 4 mins (refreshCache) of login:** When i hit **_getSession_** from client or middleware, it updates the expiry of **_session_data_** and **_account_data_** but **_session_token_** cookie remains unchanged. <img width="615" height="129" alt="Image" src="https://github.com/user-attachments/assets/8a6902f7-13d9-4555-9d81-c3a1ffe5fa31" /> **Result:** The user is logged out at exactly 5 min after login, because the main token never rotates or extends. **Expected:** **After 2 mins (updateAge) of login:** The _**session_token**_ expiry should update after calling **_getSession_** to extend the session lifetime, preventing forced logout when the user is active. ### What version of Better Auth are you using? 1.4.18 ### System info ```bash - better-auth: 1.4.18 - Node.js: v22.22.0 - npm: 11.7.0 - OS: Windows - Framework: Next.js 16.1.6 - React: 19.2.3 - TypeScript: 5.x ``` ### Which area(s) are affected? (Select all that apply) Client, Backend, Other ### Auth config (if applicable) ```typescript export const auth = betterAuth({ socialProviders: { microsoft: { clientId: process.env.APP_CLIENT_ID ?? "", clientSecret: process.env.APP_CLIENT_SECRET ?? "", tenantId: process.env.TENANT_ID ?? "", scope: MICROSOFT_AUTH_SCOPES, }, }, session: { expiresIn: 60 * 5, // 5 min updateAge: 60 * 2, // 2 min cookieCache: { enabled: true, maxAge: 60 * 5, // 5 min strategy: "jwe", refreshCache: true, // refreshes session data cookies after 4 min }, }, account: { storeStateStrategy: "cookie", // Default storeAccountCookie: true, // Default for stateless }, plugins: [nextCookies()], }); ``` ### Additional context proxy.ts- ``` export async function proxy(request: NextRequest) { const sessionResponse = await auth.api.getSession({ headers: await headers(), asResponse: true, returnHeaders: true, }); const session = await sessionResponse.json(); if (!session) { return NextResponse.redirect(new URL("/sign-in", request.url)); } const sessionHeader = sessionResponse.headers.get("set-cookie"); if (sessionHeader) { const response = NextResponse.next(); response.headers.set("set-cookie", sessionHeader); return response; } return NextResponse.next(); } ``` Client test page page.tsx ``` "use client"; import { createAuthClient } from "better-auth/react"; const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000", }); export default function Page() { const handleGetSession = async () => { const { data, error } = await authClient.getSession(); if (error) { console.error("Failed to fetch session", error); return; } console.log("Session", data); }; return ( <div className="center flex flex-col gap-2 p-4"> <div>Home Page</div> <button className={` border-gray-400 w-32 rounded border px-3 py-1 text-sm font-medium `} onClick={handleGetSession} > Get Session </button> </div> ); } ```
GiteaMirror added the lockedbug labels 2026-04-15 18:52:27 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Feb 16, 2026):

Hi @Rohit-sharma04! I'm Dosu and I’m helping the better-auth team.

This is a confirmed bug in Better Auth's stateless mode. Your analysis is correct.

Root Cause: In stateless/cookieCache mode, getSession only refreshes the session_data cookie via setCookieCache(), but the session_token cookie is never updated. The session_token refresh logic is tied to database session updates, which don't execute in stateless mode.

Since getSession requires both cookies to be present, when session_token expires at your original expiresIn (5 min), users get logged out even though session_data was being continuously refreshed.

This has been reported in Issue #2115 and Issue #7703. Maintainers acknowledge this is a systemic issue and are working on a pulse mechanism to automatically refresh cookies.

Workarounds:

  1. Use database-backed sessions (recommended) - Even a lightweight database enables the full session refresh path that updates both cookies
  2. Increase expiresIn to your maximum desired session duration (loses short-lived session security benefit)
  3. Wait for official fix - Monitor Issue #2115 for the pulse mechanism update

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3907673860 --> @dosubot[bot] commented on GitHub (Feb 16, 2026): <!-- Greeting --> Hi @Rohit-sharma04! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a **confirmed bug** in Better Auth's stateless mode. Your analysis is correct. **Root Cause:** In stateless/cookieCache mode, `getSession` only refreshes the `session_data` cookie via [`setCookieCache()`](https://github.com/better-auth/better-auth/blob/d3b579e42a707a9037a9210ad14611366b745d2a/packages/better-auth/src/api/routes/session.ts#L262-L276), but the `session_token` cookie is **never updated**. The `session_token` refresh logic is [tied to database session updates](https://github.com/better-auth/better-auth/blob/d3b579e42a707a9037a9210ad14611366b745d2a/packages/better-auth/src/api/routes/session.ts#L387-L419), which don't execute in stateless mode. Since `getSession` requires both cookies to be present, when `session_token` expires at your original `expiresIn` (5 min), users get logged out even though `session_data` was being continuously refreshed. This has been reported in [Issue #2115](https://github.com/better-auth/better-auth/issues/2115#issuecomment-3487928118) and [Issue #7703](https://github.com/better-auth/better-auth/issues/7703#issuecomment-3822989141). Maintainers acknowledge this is a systemic issue and are working on a pulse mechanism to automatically refresh cookies. **Workarounds:** 1. **Use database-backed sessions** (recommended) - Even a lightweight database enables the full session refresh path that updates both cookies 2. **Increase `expiresIn`** to your maximum desired session duration (loses short-lived session security benefit) 3. **Wait for official fix** - Monitor Issue #2115 for the pulse mechanism update <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ed1d82f2-f8f5-40f7-9585-e89c73961c18?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20%2A%2Aconfirmed%20bug%2A%2A%20in%20Better%20Auth%27s%20stateless%20mode.%20Your%20analysis%20is%20correct.%0A%0A%2A%2ARoot%20Cause%3A%2A%2A%20In%20stateless/cookieCache%20mode%2C%20%60getSession%60%20only%20refreshes%20the%20%60session_data%60%20cookie%20via%20%5B%60setCookieCache%28%29%60%5D%28https%3A//github.com/better-auth/better-auth/blob/d3b579e42a707a9037a9210ad14611366b745d2a/packages/better-auth/src/api/routes/session.ts%23L262-L276%29%2C%20but%20the%20%60session_token%60%20cookie%20is%20%2A%2Anever%20updated%2A%2A.%20The%20%60session_token%60%20refresh%20logic%20is%20%5Btied%20to%20database%20session%20updates%5D%28https%3A//github.com/better-auth/better-auth/blob/d3b579e42a707a9037a9210ad14611366b745d2a/packages/better-auth/src/api/routes/session.ts%23L387-L419%29%2C%20which%20don%27t%20execute%20in%20stateless%20mode.%0A%0ASince%20%60getSession%60%20requires%20both%20cookies%20to%20be%20present%2C%20when%20%60session_token%60%20expires%20at%20your%20original%20%60expiresIn%60%20%285%20min%29%2C%20users%20get%20logged%20out%20even%20though%20%60session_data%60%20was%20being%20continuously%20refreshed.%0A%0AThis%20has%20been%20reported%20in%20%5BIssue%20%232115%5D%28https%3A//github.com/better-auth/better-auth/issues/2115%23issuecomment-3487928118%29%20and%20%5BIssue%20%237703%5D%28https%3A//github.com/better-auth/better-auth/issues/7703%23issuecomment-3822989141%29.%20Maintainers%20acknowledge%20this%20is%20a%20systemic%20issue%20and%20are%20working%20on%20a%20pulse%20mechanism%20to%20automatically%20refresh%20cookies.%0A%0A%2A%2AWorkarounds%3A%2A%2A%0A%0A1.%20%2A%2AUse%20database-backed%20sessions%2A%2A%20%28recommended%29%20-%20Even%20a%20lightweight%20database%20enables%20the%20full%20session%20refresh%20path%20that%20updates%20both%20cookies%0A2.%20%2A%2AIncrease%20%60expiresIn%60%2A%2A%20to%20your%20maximum%20desired%20session%20duration%20%28loses%20short-lived%20session%20security%20benefit%29%0A3.%20%2A%2AWait%20for%20official%20fix%2A%2A%20-%20Monitor%20Issue%20%232115%20for%20the%20pulse%20mechanism%20update)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7994)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19598