fix: unified session identity across dev/prod for shared Neon DB#99
fix: unified session identity across dev/prod for shared Neon DB#99
Conversation
…ion email fix - /api/auth/session endpoints call getSession() instead of returning DEV_SESSION - try/catch around getSessionEmail() in dev mode for DB startup errors - Production getSession uses getSessionEmail() to return real email (was hardcoded "user") - Client and server now show same identity in all environments
✅ Deploy Preview for agent-native-fw ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
agent-native-content | 3360f2e | Commit Preview URL Branch Preview URL |
Mar 27 2026, 08:17 PM |
Add 10-minute in-memory cache for listContacts handler. Both the Google People API path and the local fallback path cache their results, preventing repeated API calls that trigger sync quota errors. Also adds email mention provider to mail template for @-tagging emails.
There was a problem hiding this comment.
Builder has reviewed your changes and found 4 potential issues.
Review Details
PR #99 Review — Unified Session Identity
This PR unifies session identity between dev and production modes when sharing a Neon/cloud database, replacing the hardcoded "user" email and DEV_SESSION fallbacks with real session lookups. The intent is correct and the dev-mode fallback logic is sound. Risk: 🔴 High — core auth changes affect all production deployments.
🔴 CRITICAL: Production auth broken for ACCESS_TOKEN logins
The PR replaces hasSession(cookie) (checks session existence only) with getSessionEmail(cookie) (checks existence + non-null email), but the mountAuthRoutes login handler calls addSession(sessionToken) without an email at line 382 — storing email = NULL in the DB. getSessionEmail returns null for NULL emails (line 135), so getSession() always returns null for ACCESS_TOKEN sessions → auth guard redirects to login → infinite loop. All three parallel review passes independently confirmed this. Google OAuth users are unaffected (their handlers pass email to addSession). Fix: pass a placeholder "user" email in the login handler — await addSession(sessionToken, "user").
🟡 MEDIUM: Cmd+R refresh leaks to background tabs
The refreshKey={isActive ? refreshKey : 0} pattern resets inactive webviews to 0 while the global counter advances. When a previously inactive tab is activated, it sees a counter jump and immediately reloads — unintentionally discarding scroll state and in-progress work.
🟡 MEDIUM: request-code-change returns fake success in production
The script returns status: "queued" with a fabricated Builder.io URL without making any real API call. The updated agent-chat.ts prompt now instructs the production agent to use this tool and tell users their change is being processed — so users receive a dead tracking link and their request is silently dropped.
🟡 MEDIUM: OAuth popup never auto-closes for hosted app URLs
openAuthWindow only closes the popup when the post-consent navigation hits localhost. The desktop app's default production apps use hosted domains (https://mail.agent-native.com), so the auth window stays open after OAuth completes in the default configuration.
✅ Correct: Dev mode try/catch, authDisabledMode fallback, chrono date fix, SnoozeModal redesign
Code review by Builder.io
| const cookie = getCookie(event, COOKIE_NAME); | ||
| if (cookie && (await hasSession(cookie))) { | ||
| return { email: "user", token: cookie }; | ||
| if (cookie) { | ||
| const email = await getSessionEmail(cookie); | ||
| if (email) return { email, token: cookie }; |
There was a problem hiding this comment.
🔴 Production auth broken: ACCESS_TOKEN sessions have null email, causing infinite login loop
The mountAuthRoutes login handler calls await addSession(sessionToken) without an email, storing email = NULL in the DB. getSessionEmail returns null for NULL emails (line 135: (rows[0].email as string) ?? null), so this block never returns a session and falls through to return null — causing every ACCESS_TOKEN-authenticated request to be treated as unauthenticated. Fix: await addSession(sessionToken, "user") in the login handler to match pre-PR behavior.
How did I do? React with 👍 or 👎 to help me improve.
| app={appDef} | ||
| appConfig={app} | ||
| isActive={isActive} | ||
| refreshKey={isActive ? refreshKey : 0} |
There was a problem hiding this comment.
🟡 refreshKey resets inactive webviews to 0, causing unintended reload on tab switch
Inactive AppWebviews receive refreshKey=0 while the global counter increments on Cmd+R. When the user later switches to an inactive tab, the prop jumps from 0 to the current counter value, triggering reloadIgnoringCache() in AppWebview's effect. Fix: pass the real refreshKey to all webviews and rely solely on the isActive guard inside AppWebview to prevent unwanted reloads.
How did I do? React with 👍 or 👎 to help me improve.
| } | ||
|
|
||
| const branchId = generateBranchId(description); | ||
| const projectId = `proj_${branchId}`; | ||
| const url = `https://builder.io/app/projects/${projectId}`; | ||
|
|
||
| const result = { | ||
| status: "queued", |
There was a problem hiding this comment.
🟡 Returns fake queued status in production without making any real API call
In production the script fabricates a projectId and Builder.io URL locally and returns status: "queued" without ever contacting a real backend. Since agent-chat.ts now instructs the production agent to call this tool for all code-change requests and tell users their change is being processed, users receive a dead tracking link and the request is silently dropped. Either wire to a real queue before enabling in production, or return an explicit "not yet available" error so the agent can respond honestly.
How did I do? React with 👍 or 👎 to help me improve.
| // After the OAuth provider redirects to localhost (the callback), | ||
| // let the request complete then close the popup. | ||
| authWin.webContents.on("did-navigate", (_event, navUrl) => { | ||
| try { | ||
| const parsed = new URL(navUrl); | ||
| if (parsed.hostname === "localhost") { |
There was a problem hiding this comment.
🟡 OAuth popup never auto-closes for hosted production app callbacks
openAuthWindow only closes when parsed.hostname === "localhost", but the desktop app's default production URLs (https://mail.agent-native.com, etc.) redirect OAuth callbacks to the hosted origin — so the BrowserWindow stays open after successful sign-in. Fix: detect the callback path (e.g. /api/google/callback) regardless of hostname instead of hard-coding localhost.
How did I do? React with 👍 or 👎 to help me improve.
Summary
getSession()checks for real session cookie in dev mode (try/catch protected)/api/auth/sessionendpoints callgetSession()instead of hardcodedDEV_SESSIONgetSessionreturns actual email from sessions table (was hardcoded"user")Test plan
local@localhostlocal@localhost🤖 Generated with Claude Code