fix: remove cross-user OAuth guard, contacts cache, Google auth fixes#103
fix: remove cross-user OAuth guard, contacts cache, Google auth fixes#103
Conversation
The "already linked to another user" check blocked Google account connections when the session identity changed (e.g. local@localhost in dev vs real email in desktop production build). Since these are single-user apps, just re-link the token to the current user.
✅ 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-mail | 953e9a6 | Commit Preview URL Branch Preview URL |
Mar 28 2026, 12:29 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
agent-native-slides | 953e9a6 | Commit Preview URL Branch Preview URL |
Mar 28 2026, 12:30 AM |
There was a problem hiding this comment.
Builder has reviewed your changes and found 3 potential issues.
Review Details
PR #103 Code Review — Risk: 🔴 High
This PR makes four changes: removes the cross-user OAuth owner guard, adds a mobile-native redirect to both mail/calendar OAuth callback handlers, hides ContactPanel during isLoading, and adds relative z-0 to the CLI terminal div.
Note: The PR description claims to add contacts API caching (10min TTL) but this was already present in the codebase prior to this PR — the description is misleading.
OAuth Guard Removal (store.ts)
The removed guard did real authorization work. Removing it makes saveOAuthTokens() an unconditional upsert that overwrites owner on every call. Two concrete problems arise:
- Token refresh paths call
saveOAuthTokens()without anownerargument, causingownerto be set toaccountId. This silently breaks multi-account scenarios where secondary accounts were linked withowner = session.email— those accounts then disappear fromgetAuthStatus()/getClients()lookups. - On shared/multi-user deployments, the last OAuth flow for a given
(provider, account_id)pair reassigns ownership, allowing one session to inadvertently take over another's linked account.
Mobile OAuth Redirect (mail + calendar)
The isMobile UA check is too broad — it matches all iOS/Android browsers, not just native app webviews. Mobile web users get sent to agentnative://oauth-complete (a custom scheme they likely don't have registered), and window.close() won't recover the tab since it's a regular OAuth redirect (not a popup). Users end up stranded despite being authenticated.
Lower-severity items
- Stale JSDoc on
saveOAuthTokensstill documents the removed "throws" behavior (low) ContactPanelwrapper removal during loading causes a 260px layout shift on lg screens (low)
Found by running 2 code-review agent calls with randomized file ordering.
Code review by Builder.io
| // If this account was previously linked to a different session identity | ||
| // (e.g. local@localhost in dev vs real email in production), just re-link it. | ||
| // These are single-user apps — no need to guard against cross-user linking. | ||
|
|
There was a problem hiding this comment.
🔴 Token refresh silently resets owner, breaking multi-account secondary accounts
Several token refresh paths (e.g., getValidAccessToken() in mail/calendar) call saveOAuthTokens() without an owner argument, so the upsert overwrites owner to accountId. Secondary accounts intentionally linked with owner = session.email disappear from getAuthStatus(session.email) / getClients(session.email) after their first token refresh. Consider preserving the existing owner on update: owner = oauth_tokens.owner unless the caller explicitly passes a new owner.
How did I do? React with 👍 or 👎 to help me improve.
| const ua = getHeader(event, "user-agent") || ""; | ||
| const isMobile = /iPhone|iPad|iPod|Android/i.test(ua); | ||
| if (isMobile) { | ||
| return new Response( | ||
| `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Connected</title></head><body style="background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p>Connected! Returning to app…</p><script>window.location.href="agentnative://oauth-complete";setTimeout(function(){window.close()},2000)</script></body></html>`, | ||
| { | ||
| status: 200, | ||
| headers: { "Content-Type": "text/html; charset=utf-8" }, |
There was a problem hiding this comment.
🟡 Mobile UA sniffing strands mobile web users after OAuth
The /iPhone|iPad|iPod|Android/i check routes ALL mobile browsers to agentnative://oauth-complete, but there's no signal proving the flow originated from the native app. Mobile web users who don't have the app installed get a dead-end page — window.close() won't recover the tab since this is a regular redirect (not a popup). Add a fallback: setTimeout(function(){ window.location.href = '/'; }, 1500) so web users recover; native app webviews intercept the custom scheme before the timeout fires.
How did I do? React with 👍 or 👎 to help me improve.
| // If this looks like a mobile request, redirect via the native app scheme | ||
| const ua = getHeader(event, "user-agent") || ""; | ||
| const isMobile = /iPhone|iPad|iPod|Android/i.test(ua); | ||
| if (isMobile) { | ||
| return new Response( | ||
| `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Connected</title></head><body style="background:#111;color:#aaa;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0"><p>Connected! Returning to app…</p><script>window.location.href="agentnative://oauth-complete";setTimeout(function(){window.close()},2000)</script></body></html>`, | ||
| { | ||
| status: 200, | ||
| headers: { "Content-Type": "text/html; charset=utf-8" }, |
There was a problem hiding this comment.
🟡 Mobile UA sniffing strands mobile web users after OAuth (calendar)
Same issue as the mail template: isMobile based on UA alone diverts all Android/iOS browsers to agentnative://oauth-complete with no fallback. Add setTimeout(function(){ window.location.href = '/'; }, 1500) after the custom-scheme redirect to recover web users who lack the native app.
How did I do? React with 👍 or 👎 to help me improve.
Summary
Test plan
🤖 Generated with Claude Code