Skip to content

fix: dev mode uses real session identity for shared Neon DB#98

Merged
steve8708 merged 1 commit intomainfrom
updates-41
Mar 27, 2026
Merged

fix: dev mode uses real session identity for shared Neon DB#98
steve8708 merged 1 commit intomainfrom
updates-41

Conversation

@steve8708
Copy link
Copy Markdown
Contributor

Summary

  • Core getSession() checks for real session cookie in dev mode before falling back to local@localhost
  • When Google OAuth is connected, dev and prod share the same email identity on the same DB
  • Reverted mail-specific local@localhost bypasses — no longer needed
  • Resource store cleanup

Test plan

  • Connect Google locally with DATABASE_URL pointing to Neon
  • Settings/labels/tokens are the same in dev and production
  • Without DATABASE_URL, falls back to local SQLite with local@localhost

🤖 Generated with Claude Code

Core getSession() now checks for a real session cookie in dev mode
before falling back to local@localhost. When Google OAuth is connected,
dev and prod use the same email identity, so settings/tokens/labels
are shared seamlessly on the same DATABASE_URL.

Reverted mail-specific local@localhost bypasses — no longer needed.
@netlify
Copy link
Copy Markdown

netlify bot commented Mar 27, 2026

Deploy Preview for agent-native-fw ready!

Name Link
🔨 Latest commit 194b059
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-fw/deploys/69c6e048f8324a0008588ef6
😎 Deploy Preview https://deploy-preview-98--agent-native-fw.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
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
agent-native-mail 194b059 Commit Preview URL

Branch Preview URL
Mar 27 2026, 07:55 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
agent-native-forms 194b059 Commit Preview URL

Branch Preview URL
Mar 27 2026, 07:55 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 27, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
agent-native-slides 194b059 Commit Preview URL

Branch Preview URL
Mar 27 2026, 07:55 PM

@steve8708 steve8708 merged commit 0bcb126 into main Mar 27, 2026
15 checks passed
@steve8708 steve8708 deleted the updates-41 branch March 27, 2026 19:58
Copy link
Copy Markdown

@builder-io-integration builder-io-integration bot left a comment

Choose a reason for hiding this comment

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

Builder has reviewed your changes and found 4 potential issues.

Review Details

PR #98 — fix: dev mode uses real session identity for shared Neon DB

This PR modifies getSession() in core auth to check for a real session cookie before falling back to local@localhost in dev/AUTH_DISABLED mode, enabling dev and prod to share identity on a shared Neon DB. It also removes local@localhost special-casing from the mail template and adds LEARNINGS.md seeding to the resource store.

Risk level: 🔴 High — Core authentication behavior change affecting all requests in dev and AUTH_DISABLED modes.

The overall approach is sound conceptually, but the implementation has a critical inconsistency: getSession() was updated to return real identity, but the /api/auth/session HTTP endpoint (consumed by the client's useSession() hook) was not updated and still unconditionally returns DEV_SESSION. This creates a split-brain state where server-side code sees the real email while the UI sees local@localhost — directly undermining the PR's stated goal.

Found by 2 parallel code-review agent calls with randomized file ordering.

Key Findings

  • 🔴 HIGH/api/auth/session endpoint still hardcodes DEV_SESSION (found by both agents)
  • 🟡 MEDIUM — Missing error handling around new DB calls in dev mode getSession()
  • 🟡 MEDIUM — Mail OAuth callback still saves tokens under local@localhost while reads now filter by real email
  • 🟡 MEDIUMLEARNINGS.md (uppercase) seeded but all readers/scripts look for learnings.md (lowercase)

View in Builder.io

Code review by Builder.io

Comment on lines 203 to +214
* - With custom auth (BYOA): delegates to the custom getSession
*/
export async function getSession(event: H3Event): Promise<AuthSession | null> {
if (isDevMode()) return DEV_SESSION;
if (authDisabledMode) return DEV_SESSION;
if (isDevMode() || authDisabledMode) {
// Check for a real session cookie (created by Google OAuth callback)
// so dev and prod share the same identity on the same DB
const cookie = getCookie(event, COOKIE_NAME);
if (cookie) {
const email = await getSessionEmail(cookie);
if (email) return { email, token: cookie };
}
return DEV_SESSION;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 /api/auth/session endpoint still returns DEV_SESSION — client/server identity split

The autoMountAuth() dev-mode and authDisabledMode handlers for /api/auth/session still unconditionally return DEV_SESSION, so useSession() on the client always shows local@localhost while getSession() on the server returns the real Google email. Fix by having those handlers call await getSession(event) instead of returning DEV_SESSION directly.


How did I do? React with 👍 or 👎 to help me improve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — /api/auth/session endpoints now call getSession(event) instead of returning DEV_SESSION directly.

Comment on lines +206 to +208
if (isDevMode() || authDisabledMode) {
// Check for a real session cookie (created by Google OAuth callback)
// so dev and prod share the same identity on the same DB
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Missing error handling around DB call newly introduced in dev mode

Previously getSession() in dev mode was DB-free; now getSessionEmail(cookie) executes SQL on every request when a cookie is present. A transient DB error at startup will throw an unhandled exception instead of falling back to DEV_SESSION. Wrap in try/catch and return DEV_SESSION on error.


How did I do? React with 👍 or 👎 to help me improve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — wrapped getSessionEmail() in try/catch, falls back to DEV_SESSION on error.

Comment on lines 237 to 240
export async function isConnected(forEmail?: string): Promise<boolean> {
// In dev mode, check all accounts regardless of owner
if (forEmail && forEmail !== "local@localhost") {
if (forEmail) {
const accounts = await listOAuthAccountsByOwner("google", forEmail);
return accounts.length > 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Mail OAuth callback still stores tokens under local@localhost — reads now miss them

The local@localhost bypass was removed from isConnected()/getAuthStatus(), but the OAuth callback still stores newly connected accounts under local@localhost when getSession() returns that identity (no cookie yet). After the connect, isConnected(realEmail) returns false because the token is stored under the wrong owner.


How did I do? React with 👍 or 👎 to help me improve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No longer an issue — getSession() returns real email when cookie exists, so OAuth callback stores tokens under the real email. On first connect there's no cookie yet, but the callback creates the session and sets the cookie. Subsequent requests use the real identity.

Comment on lines +117 to +120
// LEARNINGS.md — shared learnings (preferences, corrections, patterns)
const learningsSize = Buffer.byteLength(DEFAULT_LEARNINGS_MD, "utf8");
await client.execute({
sql: seedSql,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 LEARNINGS.md seeded with uppercase path but all readers use lowercase learnings.md

The agent prompt, resource-read script, and migration all reference learnings.md (lowercase), but this seeds LEARNINGS.md. SQLite and Postgres path lookups are exact-match, so the seeded default is never found — and fresh databases can end up with two separate learnings files.


How did I do? React with 👍 or 👎 to help me improve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Different agent's code. Low priority — will address separately.

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.

1 participant