Cookie parsing issue in production build with getSessionCookie function #699

Closed
opened 2026-03-13 08:00:46 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @TariqueAnowar on GitHub (Feb 18, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Set up a Next.js project with better-auth.
  2. Add middleware to check session:

Code Snippet

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth";

export function middleware(request: NextRequest) {
  const sessionCookie = getSessionCookie(request, {
    cookieName: "session_token",
    cookiePrefix: "better-auth"
  });

  if (!sessionCookie) {
    const redirectResponse = NextResponse.redirect(
      new URL("/login", request.url)
    );
    redirectResponse.headers.set("x-middleware-cache", "no-cache");
    return redirectResponse;
  }

  return NextResponse.next();
}



### Current vs. Expected behavior

getSessionCookie function returns null inproduction with config object or without. works perfectly in dev mode.

The getSessionCookie function fails to parse cookies in production builds (npm run start) while working correctly in development (npm run dev). The issue appears to be caused by an undefined variable al in the minified code.

Expected behavior
The getSessionCookie function should correctly parse and return the session cookie in both development and production environments.

Actual behavior
Development: Works correctly, returns the session cookie.
Production: Returns null, even though the cookie is present in the request headers.

The cookie is present in headers: "better-auth.session_token=wQbfYY7OC6SkNaSrx63w7yrW2Unp2WCn.yw37B3fnA2WlkkFa2W5dr28Dj7buVLyTIsbKnM0LA3g%3D"

### What version of Better Auth are you using?

1.1.18

### Provide environment information

```bash
Next.js version: 15.1.6
"react": "^18.3.1",
Operating System: macOS/Windows/Linux

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { admin } from "better-auth/plugins";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { prisma } from "./prisma-client";
import emailService from "@/services/email.service";

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
  },
  emailVerification: {
    sendVerificationEmail: async ({ user, url, token }, request) => {
      await emailService.sendVerificationEmail(user.email, url);
    },
  },
  plugins: [admin({ impersonationSessionDuration: 60 * 60 })],
});

Additional context

Claude (AI) suggestrion

The issue is that al is undefined in the minified code, but it's being used in a ternary condition. This is likely a minification issue where a variable name got mangled.

(e,t)=>{
    let r=(e instanceof Headers?e:e.headers).get("cookie");
    if(!r)return null;
    let{cookieName:n="session_token",cookiePrefix:i="better-auth"}=t||{},
    a=al?`__Secure-${i}.${n}`:`${i}.${n}`;  // <-- Problem is here
    return(function(e){
        let t=e.split("; "),r=new Map;
        return t.forEach(e=>{let[t,n]=e.split("=");r.set(t,n)}),r
    })(r).get(a)||null
}
Originally created by @TariqueAnowar on GitHub (Feb 18, 2025). ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce 1. Set up a Next.js project with better-auth. 2. Add middleware to check session: **Code Snippet** ```ts import { NextRequest, NextResponse } from "next/server"; import { getSessionCookie } from "better-auth"; export function middleware(request: NextRequest) { const sessionCookie = getSessionCookie(request, { cookieName: "session_token", cookiePrefix: "better-auth" }); if (!sessionCookie) { const redirectResponse = NextResponse.redirect( new URL("/login", request.url) ); redirectResponse.headers.set("x-middleware-cache", "no-cache"); return redirectResponse; } return NextResponse.next(); } ### Current vs. Expected behavior getSessionCookie function returns null inproduction with config object or without. works perfectly in dev mode. The getSessionCookie function fails to parse cookies in production builds (npm run start) while working correctly in development (npm run dev). The issue appears to be caused by an undefined variable al in the minified code. Expected behavior The getSessionCookie function should correctly parse and return the session cookie in both development and production environments. Actual behavior Development: Works correctly, returns the session cookie. Production: Returns null, even though the cookie is present in the request headers. The cookie is present in headers: "better-auth.session_token=wQbfYY7OC6SkNaSrx63w7yrW2Unp2WCn.yw37B3fnA2WlkkFa2W5dr28Dj7buVLyTIsbKnM0LA3g%3D" ### What version of Better Auth are you using? 1.1.18 ### Provide environment information ```bash Next.js version: 15.1.6 "react": "^18.3.1", Operating System: macOS/Windows/Linux ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { admin } from "better-auth/plugins"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { prisma } from "./prisma-client"; import emailService from "@/services/email.service"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, emailVerification: { sendVerificationEmail: async ({ user, url, token }, request) => { await emailService.sendVerificationEmail(user.email, url); }, }, plugins: [admin({ impersonationSessionDuration: 60 * 60 })], }); ``` ### Additional context ### Claude (AI) suggestrion The issue is that al is undefined in the minified code, but it's being used in a ternary condition. This is likely a minification issue where a variable name got mangled. ```ts (e,t)=>{ let r=(e instanceof Headers?e:e.headers).get("cookie"); if(!r)return null; let{cookieName:n="session_token",cookiePrefix:i="better-auth"}=t||{}, a=al?`__Secure-${i}.${n}`:`${i}.${n}`; // <-- Problem is here return(function(e){ let t=e.split("; "),r=new Map; return t.forEach(e=>{let[t,n]=e.split("=");r.set(t,n)}),r })(r).get(a)||null }
GiteaMirror added the bug label 2026-03-13 08:00:46 -05:00
Author
Owner

@gaboprieto commented on GitHub (Feb 20, 2025):

Any temporary fix for this?

@gaboprieto commented on GitHub (Feb 20, 2025): Any temporary fix for this?
Author
Owner

@jimmysafe commented on GitHub (Feb 20, 2025):

same issue here, deployed on vercel, getSessionCookie is null. works fine in dev mode.

@jimmysafe commented on GitHub (Feb 20, 2025): same issue here, deployed on vercel, `getSessionCookie` is null. works fine in dev mode.
Author
Owner

@furkan-asani commented on GitHub (Feb 20, 2025):

You can either fix it by using the secure option like this: https://better-auth.vercel.app/docs/concepts/cookies#secure-cookies
Or you could include a baseURL in your auth config which starts with 'https://' for your production environment.
If you are deploying to Vercel and are using the VERCEL_URL env variable then you need to prepend 'https://' as a prefix since otherwise it wont be able to fetch the cookie successfully.

@furkan-asani commented on GitHub (Feb 20, 2025): You can either fix it by using the secure option like this: https://better-auth.vercel.app/docs/concepts/cookies#secure-cookies Or you could include a baseURL in your auth config which starts with 'https://' for your production environment. If you are deploying to Vercel and are using the VERCEL_URL env variable then you need to prepend 'https://' as a prefix since otherwise it wont be able to fetch the cookie successfully.
Author
Owner

@ryanpwaldon commented on GitHub (Mar 7, 2025):

There might be a deeper issue here. When cookies are created, we check useSecureCookies, baseURL, and isProduction to determine whether or not to use secure cookies. However, when we use getSessionCookie, we only check whether the app is running in production.

So, if useSecureCookies is set to false and the app is running in production, cookies won't be created with the secure prefix, however, the getSessionCookie function is still expecting a cookie with a secure prefix, so it does not return a session.

It also means if I'm running in production on http://localhost, getSessionCookie won't return a session because secure cookies cannot be sent over http.

Unsure if there was a specific reason behind why it was implemented like this?

de4dee5fdd/packages/better-auth/src/cookies/index.ts (L14-L21)

de4dee5fdd/packages/better-auth/src/cookies/index.ts (L250-L252)

Edit: I just realized getSessionCookie simply checks for the existence of a cookie with a particular name. Can easily work around by writing your own getSessionCookie function.

@ryanpwaldon commented on GitHub (Mar 7, 2025): There might be a deeper issue here. When cookies are created, we check `useSecureCookies`, `baseURL`, and `isProduction` to determine whether or not to use secure cookies. However, when we use `getSessionCookie`, we only check whether the app is running in production. So, if `useSecureCookies` is set to `false` and the app is running in production, cookies won't be created with the secure prefix, however, the getSessionCookie function is still expecting a cookie with a secure prefix, so it does not return a session. It also means if I'm running in production on `http://localhost`, getSessionCookie won't return a session because secure cookies cannot be sent over `http`. Unsure if there was a specific reason behind why it was implemented like this? https://github.com/better-auth/better-auth/blob/de4dee5fdd6462326027c34e5e4386fd41af5059/packages/better-auth/src/cookies/index.ts#L14-L21 https://github.com/better-auth/better-auth/blob/de4dee5fdd6462326027c34e5e4386fd41af5059/packages/better-auth/src/cookies/index.ts#L250-L252 Edit: I just realized `getSessionCookie` simply checks for the existence of a cookie with a particular name. Can easily work around by writing your own `getSessionCookie` function.
Author
Owner

@TariqueAnowar commented on GitHub (Mar 8, 2025):

Here is the workaround. I am using this fix in production also.

Middware.ts

import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  // Manual cookie parsing as temporary workaround
  const cookieHeader = request.headers.get("cookie");
  const cookies = cookieHeader?.split("; ").reduce((acc, cookie) => {
    const [key, value] = cookie.split("=");
    acc.set(key, value);
    return acc;
  }, new Map());

  const sessionCookie =
    cookies?.get("better-auth.session_token") ||
    cookies?.get("__Secure-better-auth.session_token");

  if (!sessionCookie) {
    const redirectResponse = NextResponse.redirect(
      new URL("/login", request.url)
    );
    redirectResponse.headers.set("x-middleware-cache", "no-cache");
    return redirectResponse;
  }

  const response = NextResponse.next();
  // Enable only to test the one tap login in localhost
  //response.headers.set("Referrer-Policy", "no-referrer-when-downgrade");
  return response;
}

export const config = {
  matcher: [
    "/((?!login|register|forget-password|verify-email|reset-password|api|_next/static|_next/image|favicon.ico|images).*)",
  ],
};

@TariqueAnowar commented on GitHub (Mar 8, 2025): ## Here is the workaround. I am using this fix in production also. ### Middware.ts ```typescript import { NextRequest, NextResponse } from "next/server"; export function middleware(request: NextRequest) { // Manual cookie parsing as temporary workaround const cookieHeader = request.headers.get("cookie"); const cookies = cookieHeader?.split("; ").reduce((acc, cookie) => { const [key, value] = cookie.split("="); acc.set(key, value); return acc; }, new Map()); const sessionCookie = cookies?.get("better-auth.session_token") || cookies?.get("__Secure-better-auth.session_token"); if (!sessionCookie) { const redirectResponse = NextResponse.redirect( new URL("/login", request.url) ); redirectResponse.headers.set("x-middleware-cache", "no-cache"); return redirectResponse; } const response = NextResponse.next(); // Enable only to test the one tap login in localhost //response.headers.set("Referrer-Policy", "no-referrer-when-downgrade"); return response; } export const config = { matcher: [ "/((?!login|register|forget-password|verify-email|reset-password|api|_next/static|_next/image|favicon.ico|images).*)", ], }; ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#699