Public API v1 with OAuth 2.0 and developer documentation at /docs - #408
Public API v1 with OAuth 2.0 and developer documentation at /docs#408hugodemenez wants to merge 12 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
37e28bc to
0834036
Compare
- OAuth authorization code flow with PKCE, refresh tokens, and revocation - Personal access tokens and OAuth app management UI at /dashboard/developers - /api/v1: me, trades (list/create with dedupe), accounts (+metrics), connections (IBKR Flex connect + sync), file imports (AI parsing and platform parsers), and metrics (summary/equity/accounts) - Tokens stored as SHA-256 hashes; scope enforcement per endpoint - First-party OAuth discovery metadata and full OpenAPI 3.1 document Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
- Bilingual (EN/FR) MDX docs: overview, authentication, trades, accounts, connections, imports, metrics, and errors - Landing-style docs index and article pages with TOC and prev/next nav - Navbar Developers menu, footer, and sitemap now link to the docs - Proxy: treat /docs as public and only redirect the exact /authentication path for signed-in users Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
- /oauth/authorize: exclude /oauth/ from i18n locale routing so the consent page resolves (it lives outside the [locale] tree) - /api/v1/metrics/equity: pre-filter trades by from/to since computeEquityChartData only applies its date filter in per-account mode - globals.css: consume shiki dual-theme variables so MDX code blocks are actually syntax highlighted in light and dark mode Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Replace the multi-page docs index and slug primary UX with one scrollable /docs page, sticky left anchors, legacy slug redirects, and stubs for the playground/OpenAPI slots. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Provide DocsOpenApiReference and DocsApiPlayground with server actions to generate/revoke a Docs playground PAT for logged-in visitors. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Next.js forbids non-async exports from "use server" modules; keep token name/scope constants module-private. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Remove leftover sibling-agent placeholder comments now that the playground and OpenAPI reference are integrated. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Without "use cache", Cache Components treated /docs as an uncached prerender and failed on unstable Date.now() in the landing tree. Restore CachedDocsPage (same pattern as updates); playground and OpenAPI stay client-side and still call APIs at runtime. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Wire scrollable table components into compileMDX so wide docs tables no longer overflow the page on mobile. Headers stay sticky inside the table scrollport; tighten OpenAPI detail padding on small screens. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Generate token already uses a server action; preview crashed (digest 802521699) because public.OAuthAccessToken was missing on the shared DB. Add the oauth tables migration and return a friendly error instead of throwing so the docs page no longer full-page fails. Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
Refactor the docs try-it UX into shared playground token helpers, inline narrative route try blocks, OpenAPI schema utilities, and code copy controls. Extend /api/v1 connections create/sync beyond IBKR Flex to Tradovate, DxFeed, and Rithmic Protocol via a shared connection-services module, and update OpenAPI plus EN/FR docs.
Move token controls into a shared drawer/sheet, put a capability TOC at the top, and add a demo token plus the remaining EN/FR docs and nav wiring.
39a3f47 to
4118b0d
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 5 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.
| codeChallengeMethod: codeChallengeMethod ?? null, | ||
| expiresAt: authorizationCodeExpiresAt(), | ||
| }, | ||
| }) |
There was a problem hiding this comment.
OAuth approve skips scope allowlist
High Severity
loadAuthorizeContext rejects scopes outside the app’s registered scopes, but approveAuthorizationAction only runs isValidScope on the form scope field and never intersects with app.scopes. Hidden-form tampering can mint an authorization code (and thus tokens) with scopes the app was never allowed to request.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.
| url.searchParams.set("error", "access_denied") | ||
| if (state) url.searchParams.set("state", state) | ||
| redirect(url.toString()) | ||
| } |
There was a problem hiding this comment.
Deny flow allows open redirect
High Severity
denyAuthorizationAction redirects to the submitted redirect_uri without checking client_id or registered redirectUris. The deny form only posts redirect_uri and state, so any URL can receive the access_denied redirect and be used for phishing.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.
| if (!userId) { | ||
| return { error: DxFeedErrorCode.USER_NOT_AUTHENTICATED } | ||
| } | ||
| const resolvedUserId = userId |
There was a problem hiding this comment.
Server actions accept arbitrary userId
High Severity
Broker sync server actions now take optional options.userId and, when set, skip session auth and write connections/tokens for that id. These modules are 'use server' exports, so a browser client can pass another user’s id and create or overwrite their broker connections.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.
| codeHash: sha256(code), | ||
| appId: app.id, | ||
| userId, | ||
| scopes, |
There was a problem hiding this comment.
OAuth tokens use auth id not User.id
High Severity
approveAuthorizationAction and developers PAT/app creation store getUserId() (Supabase auth id) on OAuthAuthorizationCode / OAuthAccessToken / OAuthApp, whose userId FK references User.id. Docs playground correctly resolves the Prisma user via auth_user_id. For legacy users where those ids differ, OAuth/PAT creation fails the FK or API queries miss trades/accounts keyed by User.id.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.
| expiresAt: accessTokenExpiresAt(), | ||
| refreshTokenExpiresAt: refreshTokenExpiresAt(), | ||
| }, | ||
| }) |
There was a problem hiding this comment.
Auth code consume is not atomic
Medium Severity
The authorization-code grant checks consumedAt, then updates it in a separate step before creating tokens. Concurrent exchanges of the same code can both pass the check, or a failure after consume can leave the code burned with no tokens issued.
Reviewed by Cursor Bugbot for commit 4118b0d. Configure here.


Summary
Public OAuth +
/api/v1API, plus single-page API docs at/docswith left-section navigation, OpenAPI reference, examples, and an authenticated try-it playground.Docs UX (this iteration)
/[locale]/docsscrolls through Try it → guides (MDX) → OpenAPI/docs/[slug]permanently redirects to/docs#slug(hash preserved via Next redirect/meta refresh)/openapi.jsonAPI (prior commits on this PR)
/api/v1for me, trades, accounts, connections, imports, metrics/openapi.jsonVerification
bun run typecheck✅OPENAI_API_KEY=dummy bun run build✅GET /api/v1/me→ 200 aslocal-dashboard-user/api/v1/me→ 200