[GH-ISSUE #7156] Cookie "Secure" flag bypassed when baseURL is not configured #28073

Closed
opened 2026-04-17 19:27:46 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @gmathieu on GitHub (Jan 6, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/7156

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a Better Auth instance WITHOUT setting baseURL:

    const auth = betterAuth({
      database: drizzleAdapter(db, { provider: 'pg' }),
      secret: process.env.BETTER_AUTH_SECRET,
      // No baseURL configured
    })
    
  2. Ensure no BETTER_AUTH_URL or NEXT_PUBLIC_BETTER_AUTH_URL env vars are set

  3. Deploy to a production environment with NODE_ENV=production served over HTTPS
    (e.g., auto-generated CloudFront URL: https://d3e55zkakxzao3.cloudfront.net/)

  4. Sign in and inspect the Set-Cookie response header

  5. Observe: The session cookie is missing the Secure flag

Current vs. Expected behavior

Current behavior:

When baseURL is not configured, getBaseURL() returns undefined.

c31c92f3d2/packages/better-auth/src/context/create-context.ts (L105)

In create-context.ts, this is converted to an empty string:

c31c92f3d2/packages/better-auth/src/context/create-context.ts (L124)

The cookie security logic then checks "" !== undefined which is true, so it evaluates "".startsWith("https://") which is false:

8f22ecfd04/packages/better-auth/src/cookies/index.ts (L26-L33)

Result: secure = false, bypassing the isProduction fallback entirely. Cookies are created WITHOUT the Secure flag even when NODE_ENV=production.

Expected behavior:

When baseURL is not configured, the cookie security should fall back to isProduction. This enables secure deployments to preview environments with auto-generated HTTPS URLs.

Discovery

Setting baseURL to undefined in create-context.ts results in a single test failure (see below). It's unclear whether this test protects nested logic or was written to match the current behavior. Given baseURL is typed as string | undefined, I would keep the value as undefined and patch the nested logic that expects an empty string.

better-auth:test: ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
better-auth:test: 
better-auth:test:  FAIL  src/context/create-context.test.ts > base context creation > edge cases > should handle baseURL as undefined
better-auth:test: AssertionError: expected undefined to be '' // Object.is equality
better-auth:test: 
better-auth:test: - Expected: 
better-auth:test: ""
better-auth:test: 
better-auth:test: + Received: 
better-auth:test: undefined
better-auth:test: 
better-auth:test:  ❯ src/context/create-context.test.ts:858:32
better-auth:test:     856|    const getDatabaseType = () => "memory";
better-auth:test:     857|    const res = await createAuthContext(adapter, opts, getDatabaseType);
better-auth:test:     858|    expect(res.options.baseURL).toBe("");
better-auth:test:        |                                ^
better-auth:test:     859|   });
better-auth:test:     860| 
better-auth:test: 
better-auth:test: ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯���⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

What version of Better Auth are you using?

1.4.10

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:45 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6030",
    "release": "25.2.0",
    "cpuCount": 11,
    "cpuModel": "Apple M3 Pro",
    "totalMemory": "18.00 GB",
    "freeMemory": "0.28 GB"
  },
  "node": {
    "version": "v22.14.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.2"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "16.0.10"
    },
    {
      "name": "react",
      "version": "19.2.1"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "8.16.3"
    },
    {
      "name": "drizzle",
      "version": "0.44.7"
    },
    {
      "name": "@neondatabase/serverless",
      "version": "1.0.2"
    }
  ],
  "betterAuth": {
    "version": "1.4.10",
    "config": null
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

No response

Originally created by @gmathieu on GitHub (Jan 6, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/7156 Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create a Better Auth instance WITHOUT setting `baseURL`: ```typescript const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg' }), secret: process.env.BETTER_AUTH_SECRET, // No baseURL configured }) ``` 2. Ensure no `BETTER_AUTH_URL` or `NEXT_PUBLIC_BETTER_AUTH_URL` env vars are set 3. Deploy to a production environment with `NODE_ENV=production` served over HTTPS (e.g., auto-generated CloudFront URL: https://d3e55zkakxzao3.cloudfront.net/) 4. Sign in and inspect the Set-Cookie response header 5. Observe: The session cookie is missing the Secure flag ### Current vs. Expected behavior **Current behavior:** When `baseURL` is not configured, `getBaseURL()` returns undefined. https://github.com/better-auth/better-auth/blob/c31c92f3d24f3ccfb6776d4634e0681c6dee2db5/packages/better-auth/src/context/create-context.ts#L105 In `create-context.ts`, this is converted to an empty string: https://github.com/better-auth/better-auth/blob/c31c92f3d24f3ccfb6776d4634e0681c6dee2db5/packages/better-auth/src/context/create-context.ts#L124 The cookie security logic then checks `"" !== undefined` which is true, so it evaluates `"".startsWith("https://")` which is false: https://github.com/better-auth/better-auth/blob/8f22ecfd0446eab4c9ba6c5667611a3ba426ed74/packages/better-auth/src/cookies/index.ts#L26-L33 Result: `secure = false`, bypassing the `isProduction` fallback entirely. Cookies are created WITHOUT the Secure flag even when `NODE_ENV=production`. **Expected behavior:** When `baseURL` is not configured, the cookie security should fall back to `isProduction`. This enables secure deployments to preview environments with auto-generated HTTPS URLs. **Discovery** Setting `baseURL` to `undefined` in `create-context.ts` results in a single test failure (see below). It's unclear whether this test protects nested logic or was written to match the current behavior. Given `baseURL` is typed as `string | undefined`, I would keep the value as `undefined` and patch the nested logic that expects an empty string. ``` better-auth:test: ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ better-auth:test: better-auth:test: FAIL src/context/create-context.test.ts > base context creation > edge cases > should handle baseURL as undefined better-auth:test: AssertionError: expected undefined to be '' // Object.is equality better-auth:test: better-auth:test: - Expected: better-auth:test: "" better-auth:test: better-auth:test: + Received: better-auth:test: undefined better-auth:test: better-auth:test: ❯ src/context/create-context.test.ts:858:32 better-auth:test: 856| const getDatabaseType = () => "memory"; better-auth:test: 857| const res = await createAuthContext(adapter, opts, getDatabaseType); better-auth:test: 858| expect(res.options.baseURL).toBe(""); better-auth:test: | ^ better-auth:test: 859| }); better-auth:test: 860| better-auth:test: better-auth:test: ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯���⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯ ``` ### What version of Better Auth are you using? 1.4.10 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:45 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6030", "release": "25.2.0", "cpuCount": 11, "cpuModel": "Apple M3 Pro", "totalMemory": "18.00 GB", "freeMemory": "0.28 GB" }, "node": { "version": "v22.14.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.2" }, "frameworks": [ { "name": "next", "version": "16.0.10" }, { "name": "react", "version": "19.2.1" } ], "databases": [ { "name": "pg", "version": "8.16.3" }, { "name": "drizzle", "version": "0.44.7" }, { "name": "@neondatabase/serverless", "version": "1.0.2" } ], "betterAuth": { "version": "1.4.10", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-17 19:27:46 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 6, 2026):

It looks like you've identified an issue where the session cookie is missing the Secure flag when the baseURL is not configured. This is indeed a valuable input for the Better Auth team since the Secure flag is crucial for maintaining security in production environments.

Current Behavior:

When baseURL isn't set, the conflicting logic in the code leads to the session cookie being created without the Secure flag, even in production environments. The Secure flag should be enabled in production to prevent cookies from being sent over insecure channels.

Expected Behavior:

The expected behavior should be that if baseURL isn't configured, the cookie security should still fall back to the isProduction state, ensuring that secure cookies are created in HTTPS environments.

Next Steps:

  1. Consider reviewing whether any changes or configurations can be made in your environment to set the baseURL or directly handle cookie settings.
  2. Open an issue on GitHub if one does not currently exist regarding this specific behavior, detailing your findings as you've done here.

If this issue is not addressed in the current version of Better Auth (v1.4.10), it may be helpful to tag @bekacru and @ping__ in your message for further assistance from the Better Auth team.

Do you have any more specifics you'd like to clarify or other issues you've encountered with Better Auth?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3716372527 --> @better-auth-agent[bot] commented on GitHub (Jan 6, 2026): It looks like you've identified an issue where the session cookie is missing the Secure flag when the `baseURL` is not configured. This is indeed a valuable input for the Better Auth team since the Secure flag is crucial for maintaining security in production environments. ### Current Behavior: When `baseURL` isn't set, the conflicting logic in the code leads to the session cookie being created without the Secure flag, even in production environments. The Secure flag should be enabled in production to prevent cookies from being sent over insecure channels. ### Expected Behavior: The expected behavior should be that if `baseURL` isn't configured, the cookie security should still fall back to the `isProduction` state, ensuring that secure cookies are created in HTTPS environments. ### Next Steps: 1. Consider reviewing whether any changes or configurations can be made in your environment to set the `baseURL` or directly handle cookie settings. 2. Open an issue on GitHub if one does not currently exist regarding this specific behavior, detailing your findings as you've done here. If this issue is not addressed in the current version of Better Auth (v1.4.10), it may be helpful to tag @bekacru and @ping__ in your message for further assistance from the Better Auth team. Do you have any more specifics you'd like to clarify or other issues you've encountered with Better Auth? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@bytaesu commented on GitHub (Jan 7, 2026):

Hi @gmathieu,

We're considering requiring baseURL to always be set in an upcoming major release. That said, this issue does seem valid.

Nice catch 😄

<!-- gh-comment-id:3717059402 --> @bytaesu commented on GitHub (Jan 7, 2026): Hi @gmathieu, We're considering requiring `baseURL` to always be set in an upcoming major release. That said, this issue does seem valid. Nice catch 😄
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28073