Skip to content

feat(mail): Builder-proxy Gmail — skip BYO Google OAuth#196

Closed
steve8708 wants to merge 8 commits intomainfrom
builder-credentials
Closed

feat(mail): Builder-proxy Gmail — skip BYO Google OAuth#196
steve8708 wants to merge 8 commits intomainfrom
builder-credentials

Conversation

@steve8708
Copy link
Copy Markdown
Contributor

Summary

Adds the "Connect Gmail via Builder" onramp so new users can reach mail's aha moment without ever opening Google Cloud Console. Instead of the 3-minute Google Cloud Console setup wizard, a user with `BUILDER_PRIVATE_KEY` set (via the existing `/cli-auth` flow) clicks one button and is connected.

Layered design: whichever credential path is configured wins, with direct Google first, Builder proxy second, and a clear "connect" CTA if neither.

Agent-native side (this PR)

  • `credential-provider.ts` (new) — generic helpers for Builder-proxy auth. Shaped so the upcoming LLM Gateway can reuse it.
  • `google-proxy.ts` (new) — `resolveGoogleTarget` + per-workspace ledger of accounts connected via Builder (stored in the shared `settings` table so every sibling app in a workspace sees the same accounts).
  • `builder-browser.ts` — `getBuilderGoogleConnectUrl` for the `/cli-auth` handoff with the full gmail / calendar / contacts scope set.
  • `core-routes-plugin.ts` — `GET /_agent-native/builder/google/status` (banner data) and `GET /_agent-native/builder/google/callback` (records the connected account after Builder finishes the OAuth exchange).
  • `templates/mail/server/lib/google-api.ts` — `gmail*` / `people*` / `calendar*` wrappers now accept `GoogleAuth = string | GoogleProxyTarget`. Direct string tokens keep the existing Google endpoints; proxy targets rewrite to `ai-services.../google/{api}/*`.
  • `templates/mail/server/lib/google-auth.ts` — `getClient` / `getClients` / `getAuthStatus` / `isConnected` all surface Builder-connected accounts alongside direct-OAuth accounts, de-duped by email.
  • `templates/mail/app/components/GoogleConnectBanner.tsx` — hero layout gets a prominent "Connect Gmail via Builder" primary CTA (visible when `BUILDER_PRIVATE_KEY` is set), with the existing BYO-keys wizard below it as a secondary option. "Free while in beta" copy.

Builder side (separate PRs, not in this PR)

Still to land in builder-internal + ai-services:

  • Multi-tenant Google OAuth app + Google verification submission
  • Token storage in `organizations_private.userGoogleTokens`
  • `ANY /google/{api}/v1/` proxy in ai-services that resolves `bpk-` → ownerId → stored Google token, refreshes if needed, forwards to the real Google API

Until those ship, agent-native gracefully degrades to the existing BYO-keys wizard.

Test plan

  • With `BUILDER_PRIVATE_KEY` set and no `GOOGLE_CLIENT_ID`, the mail hero banner shows "Connect Gmail via Builder" as the primary CTA with BYO-keys as a secondary "or set up your own keys" option below
  • With `BUILDER_PRIVATE_KEY` unset, the banner looks identical to before (regression test)
  • `GET /_agent-native/builder/google/status` returns `hasBuilderKey` + the cli-auth URL
  • Once the Builder side ships: complete the cli-auth flow → `/_agent-native/builder/google/callback?account-email=...` records the account → mail loads via the proxy
  • Multi-account: connecting a second Google account via Builder surfaces it as a separate entry in the account strip
  • `pnpm run prep` passes (all typecheck, tests, fmt green)

Design: `/Users/steve/.claude/plans/sorted-yawning-castle.md`

…ation

- documents.ts: getCurrentOwnerEmail() now reads from AsyncLocalStorage
  via getRequestUserEmail() instead of process.env (concurrent-safe)
- db.ts: migrations v5-v8 are now no-ops — the tables in v1-v4 already
  include owner_email, and ALTER TABLE ADD COLUMN IF NOT EXISTS doesn't
  work on SQLite, causing fresh installs to fail
- enterprise-workspace.md: use `create` (not `create-workspace`), show
  multi-select picker, document `add-app`, add unified-deploy section
  with same-origin auth/A2A benefits, add shared-env section, refresh
  "out of scope" now that cross-subpath SSO is solved by deploy.
- getting-started.md: lead with workspace + multi-select, mention
  `--standalone` and `add-app`.
- deployment.md: add "Workspace Deploy: One Origin, Many Apps" section
  at the top; document `APP_BASE_PATH` + workspace env layering.
Add a complete ad-hoc analysis workflow: the agent gathers data from
multiple sources (HubSpot, Gong, Slack, BigQuery, etc.), synthesizes
findings into a Markdown report, and saves a reusable artifact with
re-run instructions. Anyone can hit "Re-run" to get fresh results
delegated back to the agent.

- Actions: save-analysis, get-analysis, list-analyses, delete-analysis
- API routes: GET/DELETE /api/analyses, /api/analyses/:id
- UI: /analyses list page + /analyses/:id detail with re-run button
- Sidebar: Analyses nav link with IconReportAnalytics
- Navigation: analyses view support in navigate action + nav state hook
- Skill: .agents/skills/adhoc-analysis/SKILL.md with full workflow guide
- AGENTS.md: updated with analysis actions, common tasks, skill ref
- Also includes dev-all.ts fix for templates.ts port config path
- Restore v5-v8 ALTER TABLE migrations in content template that were
  incorrectly replaced with SELECT 1 — breaks upgrade path for databases
  created before owner_email was added to CREATE TABLE statements.
- Apply CF Workers setInterval shim to all JS chunks, not just entry —
  with ESM code splitting, chunks evaluate before entry module body,
  so the shim must be present in every file.
Another agent's refactor removed the dev auto-local fallback from
isLocalModeEnabled(). Without it, dev mode required real auth (Better
Auth or Google OAuth), causing constant logouts on server restart since
the session cookie pointed to a DB that may not persist.

Restored: in dev mode (NODE_ENV=development) without ACCESS_TOKEN or
custom auth, getSession() returns local@localhost automatically. This
is the expected dev experience — no auth setup needed.
Adds the "Connect Gmail via Builder" onramp so new users can reach mail's
aha moment without ever opening Google Cloud Console. When
BUILDER_PRIVATE_KEY is set and the user completes the Builder-hosted
OAuth flow, Gmail/People/Calendar calls go through
ai-services.builder.io/google/* instead of calling Google directly.

Agent-native side (this PR):

- credential-provider.ts — generic helpers for Builder-proxy auth:
  FeatureNotConfiguredError, hasBuilderPrivateKey, getBuilderAuthHeader,
  getBuilderProxyOrigin. Shaped so the LLM gateway layer can reuse it.
- google-proxy.ts — resolveGoogleTarget + per-workspace ledger of
  accounts connected via Builder (stored in the shared settings table
  so every sibling app in a workspace sees the same accounts).
- builder-browser.ts — getBuilderGoogleConnectUrl for the cli-auth
  handoff with the full gmail/calendar/contacts scope set.
- core-routes-plugin.ts — adds /_agent-native/builder/google/status
  (banner data) and /_agent-native/builder/google/callback (records
  the connected account after Builder finishes the OAuth exchange).
- templates/mail/server/lib/google-api.ts — gmail/people/calendar
  functions now accept GoogleAuth = string | GoogleProxyTarget. Direct
  string tokens keep the existing Google endpoints; proxy targets
  rewrite to ai-services.../google/{api}/*.
- templates/mail/server/lib/google-auth.ts — getClient / getClients /
  getAuthStatus / isConnected all now surface Builder-connected
  accounts alongside direct-OAuth accounts, de-duped by email.
- templates/mail/app/components/GoogleConnectBanner.tsx — hero layout
  gets a prominent "Connect Gmail via Builder" primary CTA (visible
  when BUILDER_PRIVATE_KEY is set) with the BYO-keys wizard below it
  as a secondary option. "Free while in beta" copy.

Builder side (separate PRs): multi-tenant Google OAuth app, token
storage in organizations_private, and /google/* proxy in ai-services
still to land. The agent-native side degrades to BYO-keys until they do.

Plan: /Users/steve/.claude/plans/sorted-yawning-castle.md
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 13, 2026

Deploy Preview for agent-native-fw ready!

Name Link
🔨 Latest commit 0d743b8
🔍 Latest deploy log https://app.netlify.com/projects/agent-native-fw/deploys/69dd5ff6302ffd0008cf543f
😎 Deploy Preview https://deploy-preview-196--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.

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 13, 2026

Deploy Preview for nutritrack-daily-calories ready!

Name Link
🔨 Latest commit 0d743b8
🔍 Latest deploy log https://app.netlify.com/projects/nutritrack-daily-calories/deploys/69dd5ff6a2d4e5000806a70d
😎 Deploy Preview https://deploy-preview-196--nutritrack-daily-calories.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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-mail with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-calendar with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-analytics with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-content with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-forms with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-dispatcher with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-slides with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying agent-native-videos with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0d743b8
Status:🚫  Deploy failed.

View logs

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 6 potential issues.

Review Details

Code Review Summary

This PR adds a "Connect Gmail via Builder" onramp to the mail template, enabling users to skip Google Cloud Console setup by using Builder's proxied OAuth. The feature spans 55 files across core framework changes, Google proxy infrastructure, mail API integration, and new UI components. The scope is significant and security-sensitive.

Risk Assessment: 🔴 HIGH

  • OAuth/credential handling (security-sensitive)
  • Multi-user workspace credential isolation
  • API proxy auth routing
  • Large scope (3238 additions, 55 files) increases complexity

Architecture Evaluation:
The overall layered approach is sound — direct Google tokens take precedence, Builder proxy is fallback, and a clear CTA guides users. The framework integration is clean with new credential-provider.ts and google-proxy.ts modules.

However, the implementation has critical flaws in data scoping and state management that must be fixed before merge:

Key Findings

🔴 CRITICAL: Data Scoping & Security

  1. Builder Google accounts leak across workspace users — Any user in a workspace can access other users' Gmail accounts via Builder proxy. Accounts are stored in a global, unscoped settings key instead of per-user keys.

  2. Mail API mutations broken for Builder accounts — The proxy auth was added to read paths but not mutation paths (archive, mark read, send, RSVP). Builder-connected accounts appear available but operations fail with 401.

  3. Attachments and inline images broken — The /api/attachments endpoint still assumes direct OAuth tokens; Builder-connected messages lose inline images and attachment downloads.

🟡 CRITICAL: UI/UX State Management

  1. Account polling breaks multi-account workflows — The polling detects any existing account instead of detecting NEW accounts, so users cannot connect additional accounts after the first one.

  2. Type mismatchbuilderStatus type missing the accounts field despite backend returning it. This blocks proper implementation of the polling fix.

  3. No disconnect support — Builder-connected accounts appear in the UI but clicking "Disconnect" silently fails; accounts stay connected.

Actions Required Before Merge:

  • Fix credential scoping: use user-scoped settings keys for Builder accounts (e.g., u:{userEmail}:builder-google-accounts)
  • Pass user email through callback handler and credential functions
  • Update disconnect() to remove Builder accounts from the ledger
  • Refactor mail handlers to use unified auth resolution (not separate token-only paths)
  • Add Builder proxy support to /api/attachments
  • Fix polling to track initial account count, not just presence
  • Add accounts field to builderStatus type definition

🧪 Browser testing: Will run after this review (PR touches mail UI and OAuth flows)

Code review by Builder.io

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.

Visual Verification

Browser testing: 4/8 passed

Test Results: 4/8 passed ⚠️

✅ TC-01: Banner visibility without Builder key - Verify Google Connect Banner displays when BUILDER_PRIVATE_KEY is not set (succeeded)

Evidence: 1 screenshot captured

⚠️ TC-02: Banner visibility with Builder key - Verify 'Connect Gmail via Builder' appears as primary when BUILDER_PRIVATE_KEY is set (couldnt_verify)

Failure: timeout — Mail app onboarding page did not render buttons/UI elements even after code changes. Unable to access GoogleConnectBanner component to verify Builder option appears. Backend endpoints work correctly but UI layer not accessible.

✅ TC-03: Status endpoint response structure - Call GET /_agent-native/builder/google/status and verify JSON response (succeeded)

Evidence: 1 screenshot captured

⚠️ TC-04: Connect button opens popup/window - Click 'Connect Gmail via Builder' button and verify OAuth window opens (couldnt_verify)

Failure: timeout — Unable to access GoogleConnectBanner component to click the Builder button. Backend confirms connectUrl is properly formatted with all required OAuth parameters (host=agent-native-google, google_scopes with Gmail/Calendar permissions)."

⚠️ TC-05: Secondary 'Set up your own keys' flow still works - Click 'Set up Google' button and verify BYO flow appears (couldnt_verify)

Failure: timeout — Unable to reach GoogleConnectBanner to test BYO flow. Backend tests confirm endpoints are operational and endpoint access is properly configured."

⚠️ TC-06: Multiple account support - Verify account list displays when accounts are connected (couldnt_verify)

Failure: timeout — Unable to access GoogleConnectBanner after account connection. Backend endpoint listBuilderGoogleAccounts() confirmed working via /_agent-native/builder/google/status returning accounts array."

✅ TC-07: Type safety and API response validation - Verify no TypeScript type errors in browser console (succeeded)

Evidence: 1 screenshot captured

✅ TC-08: Builder key presence detection - Verify hasBuilderKey flag correctly reflects BUILDER_PRIVATE_KEY environment variable (succeeded)

Evidence: 1 screenshot captured

Details

PR #196 Gmail Builder-proxy OAuth flow: 4 of 8 tests passed (backend endpoints verified), 4 couldn't verify (UI layer inaccessible). Status endpoint, response structure, and type safety all working correctly. Builder key detection properly returns false when env var not set. ConnectUrl properly formatted with all OAuth parameters. No console errors or type mismatches.

…rding

Removes the Builder-hosted multi-tenant Gmail OAuth proxy design
(resolveGoogleTarget / builder-google-accounts / GoogleConnectBanner
"Connect Gmail via Builder" CTA / /google/* proxy scaffolding).

Why: Google's CASA review for "restricted" Gmail scopes (readonly,
send, modify) would very likely reject a multi-tenant passthrough
where one verified OAuth client proxies tokens for arbitrary
downstream apps. Rather than bet Gmail availability on that review,
we're pivoting to a per-user BYO-keys flow, optionally bootstrapped
by the agent running a browser-automation onboarding (separate PR)
so users never have to touch Google Cloud Console by hand.

Kept:
- packages/core/src/server/credential-provider.ts — the generic
  FeatureNotConfiguredError / hasBuilderPrivateKey /
  getBuilderAuthHeader / getBuilderProxyOrigin helpers are still
  useful for the LLM gateway and future Builder-hosted credential
  integrations. JSDoc updated to drop the Google-proxy framing.
- The existing Builder browser / cli-auth flow
  (getBuilderBrowserConnectUrl, /builder/callback, /builder/status).
- Another agent's /builder/agents-run route, which was unstaged on
  this branch at revert time.

Removed:
- packages/core/src/server/google-proxy.ts (entire file).
- BUILDER_GOOGLE_* constants and getBuilderGoogleConnectUrl() in
  builder-browser.ts.
- /_agent-native/builder/google/status and
  /_agent-native/builder/google/callback in core-routes-plugin.ts.
- Proxy re-exports in packages/core/src/server/index.ts.
- GoogleProxyTarget / GoogleAuth plumbing in
  templates/mail/server/lib/google-api.ts and google-auth.ts;
  Gmail/People/Calendar functions go back to taking a plain
  accessToken: string.
- "Connect Gmail via Builder" button + builderStatus fetch in
  templates/mail/app/components/GoogleConnectBanner.tsx.
- New MCP client module and settings UI components
- AgentPanel, CodeRequiredDialog, ResourcesPanel improvements
- Macros agent-chat plugin updates
@steve8708
Copy link
Copy Markdown
Contributor Author

Pivoting to agent-driven onboarding (see PR #195). Transparent Gmail proxy design rejected due to Google verification policy risk; revert + new approach consolidated on updates-90.

@steve8708 steve8708 closed this Apr 13, 2026
@steve8708 steve8708 deleted the builder-credentials branch April 13, 2026 21:42
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