Enable agent-native Privy login and split human/agent auth flows#328
Conversation
Add an API-first agentlogin path that provisions Privy users, returns KYB context, and avoids non-interactive CLI hangs while keeping human login straightforward via /signin.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| walletsJson: opts.walletsJson, | ||
| adminToken: opts.adminToken, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Duplicated agent login action handler across two registrations
Low Severity
configureAgentLoginCommand omits the .action() handler, unlike configureBrowserConnectCommand which includes it. This forces the 14-property opts-to-runAgentLogin mapping to be duplicated verbatim at both auth agentlogin and top-level agentlogin registration sites. Adding or renaming an option requires updating both copies, which is fragile and inconsistent with the browser-connect pattern.
Additional Locations (1)
Use a single CTE insert for user/workspace bootstrap so circular foreign keys no longer block brand-new agentlogin runs, and document passing E2E evidence in the report.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 5 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| RETURNING id | ||
| ) | ||
| SELECT 1; | ||
| `); |
There was a problem hiding this comment.
CTE unconditionally creates orphaned workspaces on user conflict
High Severity
The bootstrapUserWorkspace CTE always executes both the user INSERT and the workspace INSERT independently. When the user already exists (due to a race condition), the user INSERT triggers ON CONFLICT DO NOTHING and produces no rows, but the inserted_workspace CTE still creates a new workspace with the random UUID. This workspace is never linked to any user's primary_workspace_id, becoming an orphaned record. The old code avoided this by checking insertedUsers.length === 0 and skipping workspace creation when the user already existed. The workspace INSERT needs to be conditional on the user INSERT succeeding (e.g., using SELECT ... FROM inserted_user instead of VALUES).
| chainType === undefined | ||
| ) { | ||
| return address; | ||
| } |
There was a problem hiding this comment.
Linked account extraction matches accounts without chain type
Low Severity
extractAddressFromLinkedAccounts treats any linked account entry with a valid Ethereum address as a match when chainType is undefined. The condition chainType === undefined means any account record lacking both chain_type and chainType fields qualifies, potentially matching non-wallet account types that happen to have an address field resembling an Ethereum address. The type === 'wallet' check alone would be more precise.
| name: keyName, | ||
| createdBy: privyDid, | ||
| expiresAt: expiresAtInput ? new Date(expiresAtInput) : undefined, | ||
| }); |
There was a problem hiding this comment.
Agent login creates duplicate API key every call
Medium Severity
The agent-login endpoint calls createApiKey unconditionally on every request without checking for existing keys. Repeated calls for the same user/workspace create unbounded duplicate API keys. Since the endpoint is designed for agent orchestration (which may retry or re-provision), this can lead to significant key proliferation in the api_keys table with no cleanup mechanism.
| } | ||
|
|
||
| return value; | ||
| } |
There was a problem hiding this comment.
Thrown error causes 500 instead of 400 response
Medium Severity
parseBeneficiaryType throws an Error on invalid input, but the route handler has no try-catch. This causes a raw 500 Internal Server Error instead of a 400 with a clear message. Every other validation in the same agent-login handler (e.g., line 1500) uses return errorResponse(...) to send a proper 400 response. The thrown error bypasses that pattern and loses the helpful error message for API callers.
Additional Locations (1)
| if (!starterAccountsCreated) { | ||
| starterAccountsSkippedReason = | ||
| 'Starter account provisioning unavailable'; | ||
| } |
There was a problem hiding this comment.
Uncaught starter-account error loses created API key
High Severity
createStarterVirtualAccounts is awaited without a try-catch, but it runs after createApiKey has already persisted a new key. If the external Align service call throws (timeout, network error, etc.), the entire request fails with a 500, and the already-created API key is never returned to the caller. The other call site in register-primary/route.ts intentionally calls this function fire-and-forget (no await), confirming it can fail. The handler's own design treats starter creation as optional (multiple skip reasons, skipped_reason response field), so an exception here contradicts that intent and causes an active credential to leak.
Ensure agent-native login creates or resolves a Privy wallet by default and add a security report comparing pure agent flows with human interactive auth.
|
Correction to previous comment (shell escaped my markdown). Follow-up is pushed in commit 24f1df2.\n\n- agentlogin now provisions a wallet by default (Ethereum) when wallets are not specified\n- existing Privy users are fetched; if no wallet address is present, wallet pre-generation is attempted automatically\n- response now includes wallet details: address, requested, provisioned, provisioning_error\n\nSecurity report added at: packages/web/test-artifacts/privy-agent-native-login/SECURITY-REPORT.md\nImplementation/E2E report updated at: packages/web/test-artifacts/privy-agent-native-login/REPORT.md\n\nLatest E2E run without --wallets-json returned a non-null wallet address and matching starter destination address. |


Summary
POST /api/cli/agent-loginAPI that provisions/reuses Privy users, issues workspace API keys, returns KYB context, and attempts starter-account setup when possiblezero auth agentloginflow (pluszero agentlogin) and harden browser connect so non-interactive sessions fail fast instead of hanging foreverLogin->/signin,Agent Login->/agent-login) and replace landing/onboarding links that previously sent users to/cli/connectWhy
kyb.status, flow link, starter account state)Detailed Behavior
POST /api/cli/agent-loginx-admin-tokenemail/phone/privy_user_idplus optional workspace/entity metadataapi_key,api_key_idworkspace_id,workspace_nameprivy_user_idkybobject (status,sub_status,flow_link,marked_done)starter_accountsobject (attempted,created,skipped_reason,destination_address)zero auth agentlogin ...andzero agentlogin ...now wrap that endpoint and persist config automaticallyzero auth connect --manual --no-browsernow exits with clear guidance in non-interactive sessions (no infinite wait/prompt hang)Login+Agent Login/agent-loginpage with API + CLI examples and KYB semanticsTesting
pnpm --filter @zero-finance/web typecheckpnpm --filter agent-bank buildnode packages/cli/dist/index.js auth agentlogin --helpnode packages/cli/dist/index.js agentlogin --helpnode packages/cli/dist/index.js auth connect --manual --no-browserPOST http://127.0.0.1:3000/api/cli/agent-loginwithout admin token returns401with{"error":"Unauthorized: invalid admin token"}Screenshots / Evidence
packages/web/test-artifacts/privy-agent-native-login/landing-login-agent-login.pngpackages/web/test-artifacts/privy-agent-native-login/agent-login-page.pngpackages/web/test-artifacts/privy-agent-native-login/signin-with-agent-login-link.pngpackages/web/test-artifacts/privy-agent-native-login/REPORT.mdLanding
Agent Login Page
Sign-in Agent Link
Note
High Risk
Adds an admin-gated provisioning endpoint that creates Privy identities and issues workspace API keys, plus changes workspace bootstrapping; mistakes could create unauthorized access or broken onboarding. Also touches CLI auth behavior in non-interactive environments.
Overview
Introduces an agent-native authentication/provisioning flow via
POST /api/cli/agent-login(admin-token required) that can create/reuse a Privy user, ensure a workspace/profile, mint a workspace API key, optionally provision a default Ethereum wallet, attempt starter virtual accounts, and return KYB + provisioning metadata.Updates the CLI to add
zero auth agentlogin(and top-levelzero agentlogin) to call the new endpoint and persist the returned API key, and hardens browser-basedconnectto fail fast in non-interactive sessions instead of hanging. The web UI now splits entrypoints into humanLogin(/signin) vsAgent Login(/agent-login), adds a new/agent-loginpage with examples, and updates landing/onboarding CTAs accordingly;ensureUserWorkspaceis adjusted to bootstrapusers+workspacesatomically via SQL to support API-only onboarding.Written by Cursor Bugbot for commit 24f1df2. This will update automatically on new commits. Configure here.