[GH-ISSUE #8325] Custom session not loading anymore on page re-focus v1.5+ #28377

Closed
opened 2026-04-17 19:48:58 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @RaenonX on GitHub (Mar 3, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8325

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Configure customSession to add custom fields to the session (e.g. activationInfo):
    customSession(async ({ session, user }) => ({
      session,
      user,
      activationInfo: await getUserActivationInfo(user.id),
    }), baseOptions)
    
  2. Use useSession() from better-auth/react on the client. The initial value correctly includes the custom field.
  3. Switch to another browser tab and switch back (triggering a visibility-change/focus event).
  4. Observe that useSession().data no longer contains activationInfo (or any other custom field added by customSession).

Current vs. Expected behavior

Current: On tab focus, createSessionRefreshManager in session-refresh.mjs re-fetches /get-session and reconstructs the session atom with only { session, user }, discarding all fields added by customSession:

// better-auth/dist/client/session-refresh.mjs
const sessionData = data?.session && data?.user ? {
    session: data.session,
    user: data.user,   // ← custom fields silently dropped
} : null;
sessionAtom.set({ ...currentSession, data: sessionData, error });

The server correctly returns { session, user, activationInfo } (confirmed via network inspection), but the client strips everything beyond session and user before committing to the atom.

Expected: The full server response (including all customSession-added fields) is forwarded to the session atom, consistent with the initial load and the /get-session endpoint result.

Regression: This worked correctly in better-auth@1.4.21. The issue was introduced in 1.5.0 and didn't fix at least till 1.5.1.

What version of Better Auth are you using?

1.5.1

System info

{
  "better-auth": "1.5.1",
  "node": "v24.2.0",
  "os": "win32 10.0.26200",
  "framework": "Next.js 16.1.6",
  "react": "19.2.4"
}

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

Client

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { customSession } from "better-auth/plugins/custom-session";

const baseOptions = {
  // ... base options without customSession
  session: {
    storeSessionInDatabase: true,
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60,
    },
  },
};

export const auth = betterAuth({
  ...baseOptions,
  plugins: [
    ...baseOptions.plugins,
    customSession(
      async ({ session, user }) => ({
        session,
        user,
        activationInfo: await getUserActivationInfo(user.id),
      }),
      baseOptions, // passed for proper client-side type inference
    ),
  ],
});

Additional context

Majority of the fields are written by Claude, but I have verified the information including the command output to be correct and matching what's happening. Searched "customSession" in Github issues and didn't see any related issues. Sorry if this is a dupe.

Below is the suggestion coming from Claude. My gut feeling is that picking props to pass explcitly helps mitigating the risk of accidentally passing unwanted data, but given how custom session plugin works to my knowledge, maybe a flag indicating custom session is in use, or a dedicated field for custom session, is needed?


The root cause is in src/client/session-refresh.ts in the fetchSessionWithRefresh function. When it fires on visibilitychange events (tab focus), it correctly fetches /get-session but then manually reconstructs sessionData as only { session, user } instead of spreading or forwarding the full response data. Compare with the initial session load via useAuthQuery, which does forward the full response.

The fix would be to spread the full data object rather than manually picking session and user:

// session-refresh.mjs — suggested fix
const sessionData = data?.session && data?.user ? data : null;

User: Double check the better auth repo to make sure no duplicated issues, and this is not a documented change for v1.5.0+

Originally created by @RaenonX on GitHub (Mar 3, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8325 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Configure `customSession` to add custom fields to the session (e.g. `activationInfo`): ```typescript customSession(async ({ session, user }) => ({ session, user, activationInfo: await getUserActivationInfo(user.id), }), baseOptions) ``` 2. Use `useSession()` from `better-auth/react` on the client. The initial value correctly includes the custom field. 3. Switch to another browser tab and switch back (triggering a visibility-change/focus event). 4. Observe that `useSession().data` no longer contains `activationInfo` (or any other custom field added by `customSession`). ### Current vs. Expected behavior **Current:** On tab focus, `createSessionRefreshManager` in session-refresh.mjs re-fetches `/get-session` and reconstructs the session atom with only `{ session, user }`, discarding all fields added by `customSession`: ```js // better-auth/dist/client/session-refresh.mjs const sessionData = data?.session && data?.user ? { session: data.session, user: data.user, // ← custom fields silently dropped } : null; sessionAtom.set({ ...currentSession, data: sessionData, error }); ``` The server correctly returns `{ session, user, activationInfo }` (confirmed via network inspection), but the client strips everything beyond `session` and `user` before committing to the atom. **Expected:** The full server response (including all `customSession`-added fields) is forwarded to the session atom, consistent with the initial load and the `/get-session` endpoint result. **Regression:** This worked correctly in `better-auth@1.4.21`. The issue was introduced in `1.5.0` and didn't fix at least till `1.5.1`. ### What version of Better Auth are you using? `1.5.1` ### System info ```bash { "better-auth": "1.5.1", "node": "v24.2.0", "os": "win32 10.0.26200", "framework": "Next.js 16.1.6", "react": "19.2.4" } ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { customSession } from "better-auth/plugins/custom-session"; const baseOptions = { // ... base options without customSession session: { storeSessionInDatabase: true, cookieCache: { enabled: true, maxAge: 5 * 60, }, }, }; export const auth = betterAuth({ ...baseOptions, plugins: [ ...baseOptions.plugins, customSession( async ({ session, user }) => ({ session, user, activationInfo: await getUserActivationInfo(user.id), }), baseOptions, // passed for proper client-side type inference ), ], }); ``` ### Additional context Majority of the fields are written by Claude, but I have verified the information including the command output to be correct and matching what's happening. Searched "customSession" in Github issues and didn't see any related issues. Sorry if this is a dupe. Below is the suggestion coming from Claude. My gut feeling is that picking props to pass explcitly helps mitigating the risk of accidentally passing unwanted data, but given how custom session plugin works to my knowledge, maybe a flag indicating custom session is in use, or a dedicated field for custom session, is needed? ------- The root cause is in `src/client/session-refresh.ts` in the `fetchSessionWithRefresh` function. When it fires on `visibilitychange` events (tab focus), it correctly fetches `/get-session` but then manually reconstructs `sessionData` as only `{ session, user }` instead of spreading or forwarding the full response `data`. Compare with the initial session load via `useAuthQuery`, which does forward the full response. The fix would be to spread the full `data` object rather than manually picking `session` and `user`: ```js // session-refresh.mjs — suggested fix const sessionData = data?.session && data?.user ? data : null; ``` User: Double check the better auth repo to make sure no duplicated issues, and this is not a documented change for v1.5.0+
GiteaMirror added the lockedbug labels 2026-04-17 19:48:58 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28377