Skip to content

fix(trading): use OAuth connection accounts for broker access#125

Merged
TradingGoose-Dev merged 13 commits into
mainfrom
staging
May 24, 2026
Merged

fix(trading): use OAuth connection accounts for broker access#125
TradingGoose-Dev merged 13 commits into
mainfrom
staging

Conversation

@TradingGoose-Dev
Copy link
Copy Markdown
Contributor

Summary

Promotes the current fork/staging changes to upstream/main.

This PR aligns trading broker access around user-owned OAuth connection accounts across portfolio identity discovery, order submission/detail, holdings, widgets, and socket portfolio streams. It also adds explicit socket portfolio polling shutdown and updates license/chart metadata plus the staging changelog.

Why

Trading flows were mixing workspace-scoped credential IDs with user-owned OAuth account IDs. Using OAuth connection accounts as the runtime broker identity keeps portfolio discovery, widget account selection, order execution, holdings, and realtime portfolio polling on the same contract.

Affected Areas

  • apps/tradinggoose
  • apps/docs
  • packages/*
  • Workflows / execution
  • Realtime / sockets
  • Market data / charting
  • Dev tooling / CI / infra
  • Documentation only
  • Other: license metadata and changelog

Issue Links( if any )

None.

Validation

cd apps/tradinggoose
bun run test lib/trading/portfolio-identities.test.ts app/api/providers/trading/portfolio-identities/route.test.ts app/api/providers/trading/order/route.test.ts 'app/api/orders/[orderId]/provider-detail/route.test.ts' socket-server/trading/portfolio-manager.test.ts tools/trading/holdings.test.ts widgets/widgets/components/trading-account-selector.test.tsx widgets/widgets/heatmap/components/body.test.tsx widgets/widgets/portfolio_snapshot/components/body.test.tsx widgets/widgets/quick_order/components/body.test.tsx

Result: 10 test files passed, 90 tests passed.

Also verified the branch diff touches no */migration/* files.

Risk / Rollout Notes

Deploy the app and socket server together because trading payloads now use OAuth token account IDs for broker selection.

Risk is concentrated in trading flows that still send old workspace credential IDs; those should fail under the new contract rather than being silently backfilled. Backout is to revert this PR and redeploy the previous app/socket server pair.

Config / Data Changes

  • Env vars added or changed: none
  • Database schema or migration impact: none
  • External services or provider behavior changed: Alpaca/Tradier access is now authorized through the authenticated user's OAuth connection account instead of workspace credential lookup

Screenshots / Video

N/A. No visual layout changes; license page copy changes are text-only.

Checklist

  • I kept the change focused and reviewed my own diff
  • I validated the change locally and documented the results above
  • I updated docs, examples, or copy if behavior/user-facing flows changed
  • I called out any env, schema, provider, or rollout impact
  • I did not include secrets, tokens, or private credentials in this PR

BWJ2310 and others added 10 commits May 23, 2026 17:39
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
fix(license): align attribution and Helm metadata
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Codex <codex@openai.com>
…h accounts and streamline portfolio identity discovery
fix(integrations): resolve trading accounts from OAuth connections
@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
studio (staging) Ready Ready Preview, Comment May 24, 2026 9:26pm

Request Review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 24, 2026

Greptile Summary

This PR migrates all trading broker identity and authorization flows from workspace-scoped credential IDs to user-owned OAuth connection account IDs (tokenAccountId). The PortfolioIdentity contract, order submission, holdings, provider-detail lookup, socket portfolio streaming, and widget account selection are all aligned to the new user-scoped connection model. The socket server also gains an explicit tradingPortfolioStreamManager.stop() call during graceful shutdown.

  • Identity contract: credentialId removed from PortfolioIdentity and replaced by tokenAccountId; authorizeTradingCredentialRequest replaced by authorizeTradingConnectionRequest (user-scoped DB query on account.userId).
  • Route simplification: portfolio-identities route drops workspaceId/workflowId parameters; identities are now scoped to the authenticated user's own OAuth connections.
  • Deployment risk: Historical order rows storing credentialId will fail provider-detail lookup under the new read contract.

Confidence Score: 4/5

Safe to deploy if app and socket server are rolled together, but provider-detail lookups on orders placed before this deploy will immediately return errors.

The new contract is internally consistent across all write and read paths. The break lives at the boundary between old persisted rows and the new read path in order-detail.ts — readOrderTokenAccountId returns null for any order whose stored request JSON carries the old credentialId key.

apps/tradinggoose/lib/trading/order-detail.ts — the read path for historical order records breaks on the credential-to-connection field rename.

Important Files Changed

Filename Overview
apps/tradinggoose/lib/trading/order-detail.ts Switched to readOrderTokenAccountId from readOrderCredentialId; orders placed before this deploy have credentialId in request JSON, so provider-detail lookups on historical records will return 404.
apps/tradinggoose/lib/trading/context.ts Replaced workspace-credential auth with user-connection auth; removed NextRequest dependency; renamed fields throughout. Logic is internally consistent.
apps/tradinggoose/lib/credentials/oauth.ts Renamed listOAuthConnectionsForUser to listOAuthConnectionAccountsForUser, replaced listOAuthCredentialAccountsForUser with resolveOAuthConnectionAccountForUser (user-scoped single-row lookup with isSignInOAuthProvider guard).
apps/tradinggoose/socket-server/trading/portfolio-manager.ts resolveTradingPortfolioContext now directly calls resolveOAuthConnectionAccountForUser; stop() correctly clears all intervals, subscribers and cache.
apps/tradinggoose/app/api/providers/trading/portfolio-identities/route.ts Dropped workspaceId/workflowId parameters and access checks; route is now authenticated-user-only, returning that user's own OAuth connections.
apps/tradinggoose/lib/trading/orders.ts Order submission drops NextRequest param, uses authorizeTradingConnectionRequest; orderHistoryRequest now stores tokenAccountId instead of credentialId.
apps/tradinggoose/lib/trading/holdings.ts Holdings dropped workspaceId/workflowId/NextRequest; authorization is now user-owned connection only.
apps/tradinggoose/hooks/queries/trading-portfolio.ts Removed workspaceId from all socket subscription payloads and guards; subscriptions now fire as soon as provider is set.
apps/tradinggoose/lib/trading/order-records.ts readOrderCredentialId replaced by readOrderTokenAccountId; credentialid removed from SECRET_KEY_EXACT_KEYS but still matched by SECRET_KEY_PATTERN so no redaction regression.
apps/tradinggoose/socket-server/index.ts Adds tradingPortfolioStreamManager.stop() in the SIGINT/SIGTERM shutdown handler; duplicate connection_error listener appears pre-existing.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Client([Client]) -->|tokenAccountId in portfolioIdentity| OrderRoute[Order Route POST]
    Client -->|provider only| IdentitiesRoute[Portfolio Identities Route GET]
    Client -->|tokenAccountId in portfolioIdentity| HoldingsRoute[Holdings Route POST]

    OrderRoute --> Auth1{Session or Internal Auth}
    Auth1 -->|Fail| E401A[401 Unauthorized]
    Auth1 -->|OK| checkWS[checkWorkspaceAccess]
    checkWS -->|Fail| E404A[404 Not Found]
    checkWS -->|OK| authConn1[authorizeTradingConnectionRequest]
    authConn1 -->|null| E404B[404 Connection Not Found]
    authConn1 -->|OK| resolveCtx1[resolveTradingProviderContext]
    resolveCtx1 --> submitOrder[submitTradingOrder stores tokenAccountId]

    IdentitiesRoute --> Auth2{Session?}
    Auth2 -->|Fail| E401B[401]
    Auth2 -->|OK| listConns[listOAuthConnectionAccountsForUser]
    listConns --> identOptions[Return PortfolioIdentity list with tokenAccountId]

    HoldingsRoute --> Auth3{Session or Internal Auth}
    Auth3 -->|Fail| E401C[401]
    Auth3 -->|OK| authConn2[authorizeTradingConnectionRequest]
    authConn2 --> resolveCtx2[resolveTradingProviderContext]
    resolveCtx2 --> getHoldings[getPortfolioDetail]

    SocketServer([Socket Server]) --> resolvePortfolioCtx[resolveTradingPortfolioContext]
    resolvePortfolioCtx --> pollBroker[Poll broker API per channel]
Loading

Reviews (2): Last reviewed commit: "Merge pull request #126 from BWJ2310-bac..." | Re-trigger Greptile

Comment on lines 61 to +67
throw new TradingServiceError('Tradier order history record is missing accountId')
}

const credentialId = readOrderCredentialId(order)
const tokenAccountId = readOrderTokenAccountId(order)
const serviceId = readOrderServiceId(order)
if (!credentialId || !serviceId) {
throw new TradingServiceError('Order history record is missing trading credential context')
if (!tokenAccountId || !serviceId) {
throw new TradingServiceError('Order history record is missing trading connection context')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Existing order records will fail provider-detail lookup after deploy

readOrderTokenAccountId reads request.tokenAccountId from the stored JSON, but every order placed before this deployment has request.credentialId there instead. Those rows will return null, hit the guard, and throw 'Order history record is missing trading connection context' — so any user trying to view provider detail on a historical order will get an error immediately after rollout.

The PR description calls out that new requests with old payloads "should fail rather than be silently backfilled," but this affects already-persisted rows whose data was written correctly under the old contract. A read-time coalesce (readOrderTokenAccountId(order) ?? readOrderCredentialId(order)) would preserve backwards-read compatibility while the field name migrates.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/tradinggoose/lib/trading/order-detail.ts
Line: 61-67

Comment:
**Existing order records will fail provider-detail lookup after deploy**

`readOrderTokenAccountId` reads `request.tokenAccountId` from the stored JSON, but every order placed before this deployment has `request.credentialId` there instead. Those rows will return `null`, hit the guard, and throw `'Order history record is missing trading connection context'` — so any user trying to view provider detail on a historical order will get an error immediately after rollout.

The PR description calls out that new requests with old payloads "should fail rather than be silently backfilled," but this affects already-persisted rows whose data was written correctly under the old contract. A read-time coalesce (`readOrderTokenAccountId(order) ?? readOrderCredentialId(order)`) would preserve backwards-read compatibility while the field name migrates.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex Fix in Claude Code Fix in Cursor

BWJ2310 and others added 3 commits May 24, 2026 14:59
Co-authored-by: Codex <codex@openai.com>
refactor(trading): remove workspaceId from portfolio flow
@TradingGoose-Dev
Copy link
Copy Markdown
Contributor Author

@greptileai

@TradingGoose-Dev TradingGoose-Dev merged commit 605fd3f into main May 24, 2026
5 checks passed
@TradingGoose-Dev TradingGoose-Dev deleted the staging branch May 24, 2026 21:35
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.

2 participants