Session Token cookie maxAge not being updated as per updateAge #312

Closed
opened 2026-03-13 07:41:29 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @Kabeer10 on GitHub (Dec 2, 2024).

Describe the bug
The maxAge of the session token cookie is not being updated when updateAge is set in the betterAuth configuration.

To Reproduce
Steps to reproduce the behavior:

  1. Set up betterAuth with the following configuration:
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    session: {
        expiresIn: 60 * 2, // 2 Min
        updateAge: 60 // 1min
    }
});
  1. Log in to create a session.
const { data, error } = await authClient.signIn.email({
  email: "test@example.com",
  password: "password1234",
});
  1. Check the session token cookie's maxAge after 1 Min of activity.
  2. Observe that the maxAge is not updated as expected. But session in DB is getting updated

Expected behavior
The maxAge of the session token cookie should be updated every 1 Min (as per updateAge) to extend the session expiration.

Desktop (please complete the following information):

  • OS: MacOS 15
  • Browser: Brave Browser
  • Better-Auth Version : 1.0.7

Additional context
The cookie gets deleted after 2Min and user gets logged out. The Expiry in DB is however updated after 1 Min.

Originally created by @Kabeer10 on GitHub (Dec 2, 2024). **Describe the bug** The `maxAge` of the session token cookie is not being updated when `updateAge` is set in the betterAuth configuration. **To Reproduce** Steps to reproduce the behavior: 1. Set up betterAuth with the following configuration: ``` import { betterAuth } from "better-auth"; export const auth = betterAuth({ session: { expiresIn: 60 * 2, // 2 Min updateAge: 60 // 1min } }); ``` 3. Log in to create a session. ``` const { data, error } = await authClient.signIn.email({ email: "test@example.com", password: "password1234", }); ``` 4. Check the session token cookie's maxAge after 1 Min of activity. 5. Observe that the maxAge is not updated as expected. **But session in DB is getting updated** **Expected behavior** The `maxAge` of the session token cookie should be updated every 1 Min (as per updateAge) to extend the session expiration. **Desktop (please complete the following information):** - OS: MacOS 15 - Browser: Brave Browser - Better-Auth Version : 1.0.7 **Additional context** The cookie gets deleted after 2Min and user gets logged out. The Expiry in DB is however updated after 1 Min.
Author
Owner

@Bekacru commented on GitHub (Dec 2, 2024):

My guess is this is happening because you're trying to call getSession on a server component (if you're using next) or somewhere it can't update the cookie. Are you, by any chance, calling getSession on a server?

@Bekacru commented on GitHub (Dec 2, 2024): My guess is this is happening because you're trying to call `getSession` on a server component (if you're using next) or somewhere it can't update the cookie. Are you, by any chance, calling `getSession` on a server?
Author
Owner

@Kabeer10 commented on GitHub (Dec 2, 2024):

im Using Astro. im calling getSession on client

//@lib/auth-client.ts

import { createAuthClient } from "better-auth/client"

export const authClient = createAuthClient({
  baseURL: env.API_URL + "/auth",
  plugins: [usernameClient()],
});

im calling getSession with help of useQuery

export default function Navbar() {
  const session = useQuery(
    {
      queryKey: ["user-session"],
      queryFn: async () => await authClient.getSession(),
    },
    queryClient
  );
  console.log(session.data);
  return <nav className="bg-background">Navbar</nav>;
}

When i inspect in network tab. The response session expiry is updated. but i dont see any set-cookie header.

Is there any other way cookie is being updated?

@Kabeer10 commented on GitHub (Dec 2, 2024): im Using Astro. im calling getSession on client ``` //@lib/auth-client.ts import { createAuthClient } from "better-auth/client" export const authClient = createAuthClient({ baseURL: env.API_URL + "/auth", plugins: [usernameClient()], }); ``` im calling getSession with help of useQuery ``` export default function Navbar() { const session = useQuery( { queryKey: ["user-session"], queryFn: async () => await authClient.getSession(), }, queryClient ); console.log(session.data); return <nav className="bg-background">Navbar</nav>; } ``` When i inspect in network tab. The response session expiry is updated. but i dont see any set-cookie header. Is there any other way cookie is being updated?
Author
Owner

@Bekacru commented on GitHub (Dec 2, 2024):

Okay, this should update it. But, the update works in your case only if getSession is called after 1 minute has passed and before 2 minutes have elapsed.

@Bekacru commented on GitHub (Dec 2, 2024): Okay, this should update it. But, the update works in your case only if `getSession` is called after 1 minute has passed and before 2 minutes have elapsed.
Author
Owner

@Kabeer10 commented on GitHub (Dec 2, 2024):

UPDATE

i get set-cookie header when
updateAge: 0

i tried setting
updateAge: 30 // 30 sec

i closely monitored the requests after 30 sec
The response expiry is updated, but no set-cookie header

@Kabeer10 commented on GitHub (Dec 2, 2024): **UPDATE** i get set-cookie header when ` updateAge: 0` i tried setting ` updateAge: 30` // 30 sec i closely monitored the requests after 30 sec The response expiry is updated, but no set-cookie header
Author
Owner

@Bekacru commented on GitHub (Dec 2, 2024):

we have a test here (https://github.com/better-auth/better-auth/blob/main/packages/better-auth/src/api/routes/session-api.test.ts#L76-L99). Just updated with the value you provided and it still passes them. I'll check later with a demo but if you have any findings let me know.

@Bekacru commented on GitHub (Dec 2, 2024): we have a test here (https://github.com/better-auth/better-auth/blob/main/packages/better-auth/src/api/routes/session-api.test.ts#L76-L99). Just updated with the value you provided and it still passes them. I'll check later with a demo but if you have any findings let me know.
Author
Owner

@Kabeer10 commented on GitHub (Dec 3, 2024):

Update

image

i found the above code in /better-auth/packages/better-auth/src/api/routes/session.ts

The logic seems to work, But however i tested the /get-session API. API is called once. But however Function seems to run twice. After debugging the values in console. The second time when the funtion runs. It has the updated session expiry and hence it fails

Current Config

 session: {
    expiresIn: 60 * 5,
    updateAge: 10,
    cookieCache: {
      enabled: false,
    },
  },
image
{
  expiresAt: 1733203915899,
  expiresIn: 300000,
  updateAge: 10000,
  currentTime: 1733203740173,
  logic: true,
}

{
  expiresAt: 1733204040173,
  expiresIn: 300000,
  updateAge: 10000,
  currentTime: 1733203740185,
  logic: false,
}

FLOW

getSession -> setSessionCookie -> getSessionFromCtx -> getSession

@Kabeer10 commented on GitHub (Dec 3, 2024): *Update* <img width="873" alt="image" src="https://github.com/user-attachments/assets/b1f9cb02-58ec-4ef2-a75e-4fcc51a45713"> i found the above code in `/better-auth/packages/better-auth/src/api/routes/session.ts` The logic seems to work, But however i tested the `/get-session` API. API is called once. But however Function seems to run twice. After debugging the values in console. The second time when the funtion runs. It has the updated session expiry and hence it fails **Current Config** ``` session: { expiresIn: 60 * 5, updateAge: 10, cookieCache: { enabled: false, }, }, ``` <img width="452" alt="image" src="https://github.com/user-attachments/assets/82f822d0-fc77-4b48-be9f-7c41e2f6ce78"> ``` { expiresAt: 1733203915899, expiresIn: 300000, updateAge: 10000, currentTime: 1733203740173, logic: true, } { expiresAt: 1733204040173, expiresIn: 300000, updateAge: 10000, currentTime: 1733203740185, logic: false, } ``` **FLOW** getSession -> setSessionCookie -> getSessionFromCtx -> **getSession**
Author
Owner

@pleunv commented on GitHub (Dec 30, 2024):

I'm a little confused, and seem to be running into the same issue where a session is extended while this is not reflected in the cookie. As a result, as soon as the original cookie expiry date is reached the session is revoked automatically.

In the image below the session's expiresAt is set to 2024-12-30T02:00:37.000Z, while the cookie at the bottom still has it set to the initial session expiry date of 2024-12-30T01:56:35.154Z. As soon as 2024-12-30T01:56:35.154Z passes, I'll be signed out.

image

Is there any workaround for this? Not getting much wiser from the discussion above or the linked PR.

@pleunv commented on GitHub (Dec 30, 2024): I'm a little confused, and seem to be running into the same issue where a session is extended while this is not reflected in the cookie. As a result, as soon as the original cookie expiry date is reached the session is revoked automatically. In the image below the session's `expiresAt` is set to `2024-12-30T02:00:37.000Z`, while the cookie at the bottom still has it set to the initial session expiry date of `2024-12-30T01:56:35.154Z`. As soon as `2024-12-30T01:56:35.154Z` passes, I'll be signed out. <img width="1356" alt="image" src="https://github.com/user-attachments/assets/0f71fee6-9046-48ad-87ca-4291645a1677" /> Is there any workaround for this? Not getting much wiser from the discussion above or the linked PR.
Author
Owner

@Bekacru commented on GitHub (Dec 30, 2024):

@pleunv If you're using Next.js, cookies may not get updated from middleware or rsc because it's not allowed. We're exploring better ways to update cookies in middleware, but currently, by default, cookies won't be updated unless you explicitly return the Set-Cookie headers.

@Bekacru commented on GitHub (Dec 30, 2024): @pleunv If you're using Next.js, cookies may not get updated from middleware or rsc because it's not allowed. We're exploring better ways to update cookies in middleware, but currently, by default, cookies won't be updated unless you explicitly return the `Set-Cookie` headers.
Author
Owner

@pleunv commented on GitHub (Dec 30, 2024):

This is with @tanstack/start, but I might be mishandling some things, just trying it out right now. I did follow the integration steps outlined in the docs.

@pleunv commented on GitHub (Dec 30, 2024): This is with `@tanstack/start`, but I might be mishandling some things, just trying it out right now. I did follow the integration steps outlined in the docs.
Author
Owner

@pleunv commented on GitHub (Dec 30, 2024):

Yeah, I was holding it wrong and had based my approach on this one: https://github.com/timoconnellaus/tanstack-starter-cameron/blob/master/app/middleware.tsx. The fundamental issue here is that set-cookie headers are being ignored, while they should be carried across.

In case anyone's watching, the way I currently achieved this in @tanstack/start is by using a (global) middleware and doing the following:

import { createMiddleware } from '@tanstack/start';
import { getHeaders, setHeader } from 'vinxi/http';
import { type Session, auth } from '../lib/auth.ts';

export const authMiddleware = createMiddleware().server(async ({ next }) => {
  const headers = new Headers(getHeaders() as HeadersInit);
  const response = await auth.api.getSession({ headers, asResponse: true });
  const setCookieHeader = response.headers.get('set-cookie');
  const data = ((await response.json()) || null) as Session | null;

  if (setCookieHeader) {
    setHeader('set-cookie', setCookieHeader);
  }

  return next({ context: { auth: data } });
});

And somewhere else:

import { registerGlobalMiddleware } from '@tanstack/start';
import { authMiddleware } from './auth.ts';

registerGlobalMiddleware({ middleware: [authMiddleware] });

Note: This is quick and dirty and could use some error handling + removal of casts. It's also possible there's a more efficient way to achieve this, I'm not sure.

@pleunv commented on GitHub (Dec 30, 2024): Yeah, I was holding it wrong and had based my approach on this one: https://github.com/timoconnellaus/tanstack-starter-cameron/blob/master/app/middleware.tsx. The fundamental issue here is that `set-cookie` headers are being ignored, while they should be carried across. In case anyone's watching, the way I currently achieved this in `@tanstack/start` is by using a (global) middleware and doing the following: ```ts import { createMiddleware } from '@tanstack/start'; import { getHeaders, setHeader } from 'vinxi/http'; import { type Session, auth } from '../lib/auth.ts'; export const authMiddleware = createMiddleware().server(async ({ next }) => { const headers = new Headers(getHeaders() as HeadersInit); const response = await auth.api.getSession({ headers, asResponse: true }); const setCookieHeader = response.headers.get('set-cookie'); const data = ((await response.json()) || null) as Session | null; if (setCookieHeader) { setHeader('set-cookie', setCookieHeader); } return next({ context: { auth: data } }); }); ``` And somewhere else: ```ts import { registerGlobalMiddleware } from '@tanstack/start'; import { authMiddleware } from './auth.ts'; registerGlobalMiddleware({ middleware: [authMiddleware] }); ``` Note: This is quick and dirty and could use some error handling + removal of casts. It's also possible there's a more efficient way to achieve this, I'm not sure.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#312