Skip to content

fix(auth): isolate session cookies per app in local dev#712

Open
zuchka wants to merge 4 commits into
mainfrom
fix/dev-cookie-isolation
Open

fix(auth): isolate session cookies per app in local dev#712
zuchka wants to merge 4 commits into
mainfrom
fix/dev-cookie-isolation

Conversation

@zuchka
Copy link
Copy Markdown
Contributor

@zuchka zuchka commented May 15, 2026

Summary

  • Two agent-native templates running side-by-side on localhost:<portA> and localhost:<portB> previously stomped on each other's session cookies, signing each other out. Browsers scope cookies by host only (RFC 6265 — not host+port), so both apps shared an_session and Better Auth's an.* cookies on the localhost cookie jar.
  • In dev only (NODE_ENV !== "production"), the framework now derives a per-app cookie suffix from npm_package_name then package.json:name when APP_NAME is unset. Templates running solo via pnpm dev automatically get an_session_<app> and Better Auth prefix an_<app>, so cookies no longer collide.
  • Production is intentionally unchanged. Switching a live deploy's cookie name would invalidate existing sessions (the server only reads COOKIE_NAME), so the fallback is gated behind NODE_ENV !== "production". Real deploys with explicit APP_NAME, COOKIE_DOMAIN, or workspace mode behave exactly as before.

Behavior matrix

Scenario COOKIE_NAME Better Auth prefix
Dev, APP_NAME unset, npm_package_name=mail an_session_mail (new) an_mail (new)
Dev, APP_NAME=calendar an_session_calendar an_calendar
COOKIE_DOMAIN=.agent-native.com an_session an
AGENT_NATIVE_WORKSPACE=1 an_session_workspace an
NODE_ENV=production, APP_NAME unset an_session an

Test plan

  • New unit tests in packages/core/src/server/auth.spec.ts cover all five scenarios in the matrix (5/5 passing, full suite 100/100)
  • pnpm --filter @agent-native/core typecheck — no errors in auth.ts or better-auth-instance.ts
  • Manual browser verification: cd templates/calendar && pnpm dev + cd templates/mail && pnpm dev in separate terminals; sign in to both in the same browser; confirm distinct cookies (an_session_calendar, an_session_mail, an_calendar.session_token, an_mail.session_token) and that signing into one no longer kicks the other out
  • Production smoke: confirm at least one already-deployed app still presents the same an_session / an.* cookie names it did before (sanity check that no live sessions are invalidated)

🤖 Generated with Claude Code

zuchka and others added 3 commits May 15, 2026 12:28
Two agent-native templates running side-by-side on localhost previously
stomped on each other's `an_session` cookie (browsers scope cookies by
host only, not host+port). In dev with no explicit APP_NAME, fall back
to `npm_package_name` then `package.json:name` so each app gets its own
`an_session_<slug>` cookie. Production is unchanged — switching the
cookie name on a live deploy would invalidate existing sessions, and
real deploys already set APP_NAME or COOKIE_DOMAIN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Better Auth's cookiePrefix was hard-coded to "an", so two dev apps on
localhost both wrote `an.session_token` / `an.session_data` / `an.csrf_token`
and signed each other out. Use the BETTER_AUTH_COOKIE_PREFIX export from
auth.ts so the prefix becomes `an_<slug>` when a dev slug is resolved,
and stays `an` in production / workspace / cross-subdomain modes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five tests for COOKIE_NAME / BETTER_AUTH_COOKIE_PREFIX:
- npm_package_name fallback in dev
- production stays unsuffixed
- explicit APP_NAME wins over the dev fallback
- COOKIE_DOMAIN keeps the shared `an_session` cookie
- workspace mode keeps `an_session_workspace`

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for agent-native-scheduling ready!

Name Link
🔨 Latest commit 7f4f76a
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-scheduling/deploys/6a075b8590291f000896b941
😎 Deploy Preview https://deploy-preview-712--agent-native-scheduling.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for agent-native-meeting-notes ready!

Name Link
🔨 Latest commit 7f4f76a
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-meeting-notes/deploys/6a075b8565b66300086b819b
😎 Deploy Preview https://deploy-preview-712--agent-native-meeting-notes.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for agent-native-voice ready!

Name Link
🔨 Latest commit 7f4f76a
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-voice/deploys/6a075b859d2bae00080ea371
😎 Deploy Preview https://deploy-preview-712--agent-native-voice.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for agent-native-images ready!

Name Link
🔨 Latest commit 7f4f76a
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-images/deploys/6a075b851335d900089f548d
😎 Deploy Preview https://deploy-preview-712--agent-native-images.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@cloudflare-workers-and-pages

This comment has been minimized.

@cloudflare-workers-and-pages

This comment has been minimized.

@cloudflare-workers-and-pages

This comment has been minimized.

@cloudflare-workers-and-pages

This comment has been minimized.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for agent-native-design ready!

Name Link
🔨 Latest commit 7f4f76a
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-design/deploys/6a075b85fac0c100086e287f
😎 Deploy Preview https://deploy-preview-712--agent-native-design.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@cloudflare-workers-and-pages

This comment has been minimized.

@cloudflare-workers-and-pages

This comment has been minimized.

@cloudflare-workers-and-pages

This comment has been minimized.

@zuchka zuchka marked this pull request as ready for review May 15, 2026 17:44
@builder-io-integration
Copy link
Copy Markdown
Contributor

builder-io-integration Bot commented May 15, 2026

Review Agent skipped this PR — @zuchka doesn't have a Builder seat in this space.

If you're @zuchka: you may already have a seat under a different GitHub account. Reconnect GitHub

If you're an admin: Add @zuchka to this space

Copy link
Copy Markdown
Contributor

@steve8708 steve8708 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for chasing this down. I agree with the root cause here: localhost cookies are host-scoped, not port-scoped, so two standalone dev apps on localhost:* need distinct cookie names/prefixes.

I think we need one more pass before merging, though. The PR says production is unchanged, but BETTER_AUTH_COOKIE_PREFIX is derived from APP_NAME_SLUG whenever APP_NAME is set and neither workspace mode nor COOKIE_DOMAIN is set. Today Better Auth is hard-coded to cookiePrefix: "an", so any deployed standalone app with APP_NAME=mail / calendar / etc. would rename Better Auth cookies from an.session_token to an_mail.session_token. That invalidates existing Better Auth sessions and is a production behavior change.

I’d suggest separating the cookie/auth “realm” from APP_NAME, since APP_NAME is currently carrying too many meanings: DB env prefix, app identity, workspace child id, and local cookie namespace. The invariant should be explicit:

  • standalone local dev with no shared realm: isolate cookies per app, e.g. an_session_<app> and Better Auth prefix an_<app>
  • workspace mode: shared login, e.g. an_session_workspace and Better Auth prefix an
  • COOKIE_DOMAIN mode: shared cross-subdomain login, e.g. an_session and Better Auth prefix an
  • production standalone: keep Better Auth prefix an unless we intentionally ship a session migration

Concretely, I’d put the namespace resolution in a small leaf helper used by both auth.ts and better-auth-instance.ts, so better-auth-instance.ts doesn’t need to import auth.ts:

const sharedRealm = hasCookieDomain || isWorkspaceMode;
const localIsolatedRealm = process.env.NODE_ENV !== "production" && !sharedRealm;
const slug = explicitAppSlug || (localIsolatedRealm ? packageNameSlug : "");

COOKIE_NAME = hasCookieDomain
  ? "an_session"
  : isWorkspaceMode
    ? "an_session_workspace"
    : slug
      ? `an_session_${slug}`
      : "an_session";

BETTER_AUTH_COOKIE_PREFIX = localIsolatedRealm && slug ? `an_${slug}` : "an";

Can we also add tests for the cases that previously regressed?

  • NODE_ENV=production, APP_NAME=mail, no COOKIE_DOMAIN, no workspace: COOKIE_NAME may remain app-suffixed if that is intentional, but Better Auth prefix should stay an
  • AGENT_NATIVE_WORKSPACE=1 plus APP_NAME=mail: shared an_session_workspace, Better Auth prefix an
  • COOKIE_DOMAIN=.agent-native.com plus APP_NAME=mail: shared an_session, Better Auth prefix an

One related thing to verify: workspace/shared-login only works if the shared realm also has the same Better Auth signing secret and DB. Workspace deploys/root .env appear designed for that, but local workspace dev can get weird if each app auto-generates its own .env.local BETTER_AUTH_SECRET. If that can still happen, we should either generate/read the secret at the workspace root in workspace mode or warn loudly when workspace mode has no shared BETTER_AUTH_SECRET.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants