[GH-ISSUE #2705] auth.api.getSession does not update session in asResponse #26633

Closed
opened 2026-04-17 17:16:12 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @mhornbacher on GitHub (May 18, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2705

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a NextJS app in canary mode so that node middleware can be turned on
  2. Set session.updateAge to be 1 (every 1 second)
  3. add the following code: ```typescript
    const sessionResponse = await auth.api.getSession({
    headers: request.headers,
    asResponse: true,
    });
  4. Inspect the sessionResponse headers

Current vs. Expected behavior

It should include set-cookie. It only includes application-type

What version of Better Auth are you using?

1.2.8

Provide environment information

- OS: MacOS
- Bundler: Turbopack

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

Backend

Auth config (if applicable)

import { betterAuth, type BetterAuthOptions } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { nextCookies } from "better-auth/next-js";
import { sendEmail } from "~/emails/sendEmail";
import ResetPasswordEmail from "~/emails/templates/ResetPasswordEmail";

import {
  admin,
  customSession,
  oAuthProxy,
  genericOAuth,
} from "better-auth/plugins";
import { userInfoPlugin } from "./plugins/user-info";
import { passkey } from "better-auth/plugins/passkey";

import type { Language, Role } from "@kiddom/db/enums";
import { env } from "~/library/env";
import { db } from "~/library/db";

import { headers } from "next/headers";
import { cache } from "react";
import { getBaseUrl } from "../utils/getBaseUrl";
import { Session } from "./types";

const options = {
  appName: "Kiddom",
  baseURL: getBaseUrl(),
  database: drizzleAdapter(db, {
    provider: "pg",
    usePlural: true,
  }),
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    updateAge: 1, // 1 seconds
  },
  emailAndPassword: {
    enabled: true,
    sendResetPassword: async ({ user, url }) => {
      return sendEmail(
        "noreply@kiddom.co",
        user.email,
        "Reset your Kiddom Password",
        <ResetPasswordEmail user={user.name} url={url} />,
      );
    },
  },
  socialProviders: {
    google: {
      clientId: env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
      redirectURI: `${env.OAUTH_PROXY_URL}/callback/google`,
    },
  },
  advanced: {
    cookiePrefix: "kiddom.auth",
  },
  plugins: [
    oAuthProxy(),
    admin({
      defaultRole: "student" as Role,
      adminRoles: ["admin"],
    }),
    genericOAuth({
      config: [
        { providerId: "clever", clientId: "", clientSecret: "" },
        { providerId: "edlink", clientId: "", clientSecret: "" },
      ],
    }),
    passkey(),
    userInfoPlugin(),
  ],
} satisfies BetterAuthOptions;

export const auth = betterAuth({
  ...options,
  plugins: [
    ...options.plugins,
    customSession(({ user, session }) => {
      return Promise.resolve({
        user: {
          ...user,
          role: user.role as Role,
          locale: user.locale as Language,
          nickname: user.nickname ?? undefined,
        },
        session: { ...session },
      });
    }, options),
    nextCookies(),
  ],
});

// cache getting the session per request
export const getSession = cache(async function getSession() {
  const headerStore = await headers();
  if (headerStore.get("x-kiddom-session")) {
    return JSON.parse(headerStore.get("x-kiddom-session") ?? "") as Session;
  } else {
    return auth.api.getSession({
      headers: await headers(),
    });
  }
});

Additional context

No response

Originally created by @mhornbacher on GitHub (May 18, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2705 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create a NextJS app in canary mode so that node middleware can be turned on 2. Set `session.updateAge` to be `1` (every 1 second) 3. add the following code: ```typescript const sessionResponse = await auth.api.getSession({ headers: request.headers, asResponse: true, }); ``` 3. Inspect the `sessionResponse` headers ### Current vs. Expected behavior It should include `set-cookie`. It only includes `application-type` ### What version of Better Auth are you using? 1.2.8 ### Provide environment information ```bash - OS: MacOS - Bundler: Turbopack ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth, type BetterAuthOptions } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { nextCookies } from "better-auth/next-js"; import { sendEmail } from "~/emails/sendEmail"; import ResetPasswordEmail from "~/emails/templates/ResetPasswordEmail"; import { admin, customSession, oAuthProxy, genericOAuth, } from "better-auth/plugins"; import { userInfoPlugin } from "./plugins/user-info"; import { passkey } from "better-auth/plugins/passkey"; import type { Language, Role } from "@kiddom/db/enums"; import { env } from "~/library/env"; import { db } from "~/library/db"; import { headers } from "next/headers"; import { cache } from "react"; import { getBaseUrl } from "../utils/getBaseUrl"; import { Session } from "./types"; const options = { appName: "Kiddom", baseURL: getBaseUrl(), database: drizzleAdapter(db, { provider: "pg", usePlural: true, }), session: { expiresIn: 60 * 60 * 24 * 7, // 7 days updateAge: 1, // 1 seconds }, emailAndPassword: { enabled: true, sendResetPassword: async ({ user, url }) => { return sendEmail( "noreply@kiddom.co", user.email, "Reset your Kiddom Password", <ResetPasswordEmail user={user.name} url={url} />, ); }, }, socialProviders: { google: { clientId: env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, redirectURI: `${env.OAUTH_PROXY_URL}/callback/google`, }, }, advanced: { cookiePrefix: "kiddom.auth", }, plugins: [ oAuthProxy(), admin({ defaultRole: "student" as Role, adminRoles: ["admin"], }), genericOAuth({ config: [ { providerId: "clever", clientId: "", clientSecret: "" }, { providerId: "edlink", clientId: "", clientSecret: "" }, ], }), passkey(), userInfoPlugin(), ], } satisfies BetterAuthOptions; export const auth = betterAuth({ ...options, plugins: [ ...options.plugins, customSession(({ user, session }) => { return Promise.resolve({ user: { ...user, role: user.role as Role, locale: user.locale as Language, nickname: user.nickname ?? undefined, }, session: { ...session }, }); }, options), nextCookies(), ], }); // cache getting the session per request export const getSession = cache(async function getSession() { const headerStore = await headers(); if (headerStore.get("x-kiddom-session")) { return JSON.parse(headerStore.get("x-kiddom-session") ?? "") as Session; } else { return auth.api.getSession({ headers: await headers(), }); } }); ``` ### Additional context _No response_
GiteaMirror added the locked label 2026-04-17 17:16:12 -05:00
Author
Owner

@aynaash commented on GitHub (May 29, 2025):

Any working on this?

<!-- gh-comment-id:2918433202 --> @aynaash commented on GitHub (May 29, 2025): Any working on this?
Author
Owner

@mhornbacher commented on GitHub (Jun 8, 2025):

Does not seem so. Honestly I am just writing my own auth layer at this point. There are too many gotchas with all of these 'one size fits all' tooling.

IMHO Vercel should really bundle an auth layer with NextJS for platform consistency and reputation

<!-- gh-comment-id:2953491663 --> @mhornbacher commented on GitHub (Jun 8, 2025): Does not seem so. Honestly I am just writing my own auth layer at this point. There are too many gotchas with all of these 'one size fits all' tooling. IMHO Vercel should really bundle an auth layer with NextJS for platform consistency and reputation
Author
Owner

@ping-maxwell commented on GitHub (Jul 10, 2025):

getSession would only return setSession when the session expires.

<!-- gh-comment-id:3057339819 --> @ping-maxwell commented on GitHub (Jul 10, 2025): getSession would only return setSession when the session expires.
Author
Owner

@mhornbacher commented on GitHub (Jul 10, 2025):

@ping-maxwell Not according to the implementation in session.ts. It updates the session when the update age is hit. I can validate that working by loading the session via the client or the endpoint directly.

<!-- gh-comment-id:3058331475 --> @mhornbacher commented on GitHub (Jul 10, 2025): @ping-maxwell Not according to [the implementation in session.ts](https://github.com/better-auth/better-auth/tree/main/packages/better-auth/src/api/routes/session.ts#L182-L239). It updates the session when the update age is hit. I can validate that working by loading the session via the client or the endpoint directly.
Author
Owner

@ping-maxwell commented on GitHub (Jul 10, 2025):

I apologies, I didn't see that you mentioned you had changed the default update age.

After testing locally, everything works as expected on my end.

Image Image

Are you sure you're not hitting the endpoint before your middleware runs? because it's possible the get-session is getting called and updating it in time before your real request (the one you're testing) gets to and thus you don't get the get-session headers.

<!-- gh-comment-id:3058663278 --> @ping-maxwell commented on GitHub (Jul 10, 2025): I apologies, I didn't see that you mentioned you had changed the default update age. After testing locally, everything works as expected on my end. <img width="368" height="156" alt="Image" src="https://github.com/user-attachments/assets/5eccde31-5072-4eec-901f-2a9d82c429b0" /> <img width="741" height="71" alt="Image" src="https://github.com/user-attachments/assets/cb6cc58d-d73f-4ebc-90c4-607f19e36e6e" /> Are you sure you're not hitting the endpoint before your middleware runs? because it's possible the get-session is getting called and updating it in time before your real request (the one you're testing) gets to and thus you don't get the get-session headers.
Author
Owner

@mhornbacher commented on GitHub (Jul 23, 2025):

No idea why but updating everything managed to get the above example working again :)

But it broke the age configurations in the database so now all my users would be logged out after a day regardless of what they do :(

Another issue for another day

<!-- gh-comment-id:3108986800 --> @mhornbacher commented on GitHub (Jul 23, 2025): No idea why but updating everything managed to get the above example working again :) But it broke the age configurations in the database so now all my users would be logged out after a day regardless of what they do :( Another issue for another day
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#26633