Skip to content

feat: remove API key requirement from public API#11984

Closed
0xApotheosis wants to merge 1 commit intodevelopfrom
feat/remove-public-api-key-requirement
Closed

feat: remove API key requirement from public API#11984
0xApotheosis wants to merge 1 commit intodevelopfrom
feat/remove-public-api-key-requirement

Conversation

@0xApotheosis
Copy link
Member

@0xApotheosis 0xApotheosis commented Feb 23, 2026

Description

Remove the API key requirement from the public API. The req.partner data attached by the auth middleware was never consumed by any route handler — it only served as an access gate without functional value. This makes the API truly public and simplifies integration for consumers including the swap widget.

Changes:

  • Delete packages/public-api/src/middleware/auth.ts
  • Remove STATIC_API_KEYS from config, PartnerConfig type, and Express Request augmentation
  • Remove auth middleware from all route definitions in index.ts
  • Remove apiKeyAuth security scheme and security annotations from OpenAPI spec
  • Remove auth config from Scalar docs UI
  • Remove apiKey prop from swap widget (SwapWidgetProps, ApiClientConfig, SwapWidget component, demo app)
  • Update swap widget README to remove all apiKey references
  • Update standalone mock server and smoke tests to remove auth
  • Remove auth-check smoke tests (rates/quote 401 tests)

Risk

Low. This is purely removing an unused access gate — no route handler logic changes. The API endpoints themselves are unchanged. The swap widget already worked with or without the key (it was optional in props).

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

None. This only affects HTTP API access control and widget configuration.

Testing

Engineering

  1. Build and start the public API: yarn workspace @shapeshiftoss/public-api build:bundle && yarn workspace @shapeshiftoss/public-api start:prod
  2. Verify swap endpoints work without an API key:
    curl "http://localhost:3001/v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000"
    
  3. Verify docs UI at http://localhost:3001/docs has no Authentication section
  4. Verify yarn lint and yarn type-check pass

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Verify that the public API docs page (/docs) no longer shows an Authentication section, and that swap endpoints respond without requiring an X-API-Key header.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • API Changes

    • Removed API key authentication requirement from v1 swap endpoints (rates and quotes)
    • Endpoints are now publicly accessible without authentication headers
  • Documentation

    • Updated swap widget documentation and examples to remove API key parameter
    • Removed API key configuration from guides and usage examples

The partner info attached via req.partner was never used in any route
handler — it only served as an access gate. Removing it makes the API
truly public and simplifies integration for consumers including the
swap widget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@0xApotheosis 0xApotheosis requested a review from a team as a code owner February 23, 2026 05:47
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

This PR removes API key-based authentication from the public API and swap widget packages. It deletes the STATIC_API_KEYS configuration, auth middleware functions, OpenAPI security schemes, and apiKey properties from client and component configurations. Routes and components now operate without authentication validation or header requirements.

Changes

Cohort / File(s) Summary
Auth Middleware & Core Types
packages/public-api/src/middleware/auth.ts, packages/public-api/src/types.ts, packages/public-api/src/config.ts
Removed apiKeyAuth and optionalApiKeyAuth middleware functions, PartnerConfig type definition, STATIC_API_KEYS constant, and Express.Request augmentation for partner property.
Public API Routes & Server
packages/public-api/src/index.ts, packages/public-api/src/server-standalone.ts
Removed apiKeyAuth/optionalApiKeyAuth imports and middleware usage from v1 routes. Added explicit parameter validation for swap endpoints. Updated startup logging to remove auth-related instructions.
OpenAPI Documentation
packages/public-api/src/docs/openapi.ts, packages/public-api/src/routes/docs.ts
Removed apiKeyAuth security scheme registration, security requirements from /v1/swap endpoints, authentication section from OpenAPI document, and test API key from docs UI configuration.
Public API Tests
packages/public-api/tests/smoke-tests.ts, packages/public-api/tests/test-config.ts
Removed authentication-related test cases, TEST_API_KEY constant, and API key header injections from rate/quote fetch requests.
Swap Widget API Client
packages/swap-widget/src/api/client.ts, packages/swap-widget/src/types/index.ts
Removed apiKey field from ApiClientConfig interface and eliminated x-api-key header injection logic from client requests.
Swap Widget Components
packages/swap-widget/src/components/SwapWidget.tsx, packages/swap-widget/src/demo/App.tsx
Removed apiKey parameter from createApiClient calls and updated component memoization dependencies to exclude apiKey.
Widget Documentation
packages/swap-widget/README.md
Removed apiKey prop from SwapWidget usage examples, Props Reference table, and Notes & Limitations section.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

  • feat(public-api): add chains endpoint #11737: Adds chain endpoints using optionalApiKeyAuth while this PR removes the authentication middleware entirely—the changes directly conflict and interact with each other's authentication infrastructure.

Suggested reviewers

  • NeOMakinG

Poem

🐰 Keys no longer guard the gate,
Auth middleware meets its fate,
Swap flows free without a care,
API open, unaware!
Simpler systems, lighter load,

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: removing API key requirements from the public API, which is the primary focus across all modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/remove-public-api-key-requirement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@0xApotheosis
Copy link
Member Author

Closing, neo already tackled this here: #11959

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/public-api/src/index.ts (1)

48-59: LGTM — consider adding rate limiting now that the auth gate is gone.

The auth removal is clean. One operational note: with authentication removed, the only protection against endpoint abuse is the underlying infrastructure. If this API is exposed publicly, a lightweight rate-limiting middleware (e.g., express-rate-limit) on the v1 router would help prevent runaway costs from the downstream swapper integrations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/public-api/src/index.ts` around lines 48 - 59, Authentication was
removed leaving v1Router endpoints (v1Router.get('/swap/rates', getRates),
v1Router.post('/swap/quote', getQuote), v1Router.get('/chains', getChains),
getChainCount, getAssets, getAssetCount, getAssetById) unprotected from abuse;
add a lightweight rate-limiting middleware (e.g., express-rate-limit) and apply
it to the v1Router (or specifically to swap endpoints) by creating a rateLimit
instance with sensible defaults (windowMs, max, and a standard handler) and use
v1Router.use(rateLimit(...)) before the route registrations so all listed
handlers are throttled.
packages/public-api/tests/smoke-tests.ts (1)

88-174: Consider extracting the shared rates-test body to a helper.

Tests 7 and 8 are structurally identical — same URLSearchParams construction, same 30 s fetchWithTimeout call, same validRates filter, and the same console.warn/console.log branches. The only differences are the pair variable and the test name.

♻️ Suggested extraction
+const runRatesTest = async (
+  API_URL: string,
+  pair: { name: string; sellAssetId: string; buyAssetId: string; sellAmountCryptoBaseUnit: string },
+): Promise<void> => {
+  const params = new URLSearchParams({
+    sellAssetId: pair.sellAssetId,
+    buyAssetId: pair.buyAssetId,
+    sellAmountCryptoBaseUnit: pair.sellAmountCryptoBaseUnit,
+  })
+  const res = await fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}, 30000)
+  if (!res.ok) throw new Error(`Rates request failed: ${res.status}`)
+  const data = (await res.json()) as {
+    rates?: { swapperName: string; error?: unknown; buyAmountCryptoBaseUnit?: string }[]
+  }
+  if (!Array.isArray(data.rates)) throw new Error('Invalid rates response structure')
+  const validRates = data.rates.filter(
+    r => !r.error && r.buyAmountCryptoBaseUnit && r.buyAmountCryptoBaseUnit !== '0',
+  )
+  if (validRates.length === 0) {
+    const swappersWithErrors = data.rates.filter(r => r.error).map(r => r.swapperName).join(', ')
+    console.warn(
+      `Warning: No valid rates returned for ${pair.name}. Swappers with errors: ${swappersWithErrors || 'none'}`,
+    )
+  } else {
+    console.log(`  Found ${validRates.length} valid rate(s) from: ${validRates.map(r => r.swapperName).join(', ')}`)
+  }
+}

-  // 7. EVM Same-Chain Rates (Informative)
-  const evmPair = TEST_PAIRS.evmSameChain[0]
-  results.push(
-    await runTest(`Rates: ${evmPair.name}`, false, async () => {
-      ...
-    }),
-  )
-
-  // 8. Cross-Chain Rates (Informative)
-  const crossChainPair = TEST_PAIRS.crossChain[0]
-  results.push(
-    await runTest(`Rates: ${crossChainPair.name}`, false, async () => {
-      ...
-    }),
-  )
+  for (const [label, pair] of [
+    ['evmSameChain', TEST_PAIRS.evmSameChain[0]],
+    ['crossChain',   TEST_PAIRS.crossChain[0]],
+  ] as const) {
+    results.push(await runTest(`Rates: ${pair.name}`, false, () => runRatesTest(API_URL, pair)))
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/public-api/tests/smoke-tests.ts` around lines 88 - 174, Extract the
duplicated rates-test body into a helper like checkRatesForPair(pair, testLabel)
and call it from the two runTest invocations; the helper should (1) construct
URLSearchParams from pair.sellAssetId / pair.buyAssetId /
pair.sellAmountCryptoBaseUnit, (2) call
fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}, 30000), (3) assert
res.ok and parse JSON, (4) validate Array.isArray(data.rates), filter validRates
by !r.error && r.buyAmountCryptoBaseUnit && r.buyAmountCryptoBaseUnit !== '0',
and (5) emit the same console.warn/console.log branches. Replace the duplicated
inline async bodies for evmPair and crossChainPair with results.push(await
runTest(`Rates: ${pair.name}`, false, async () => checkRatesForPair(pair,
pair.name))). Ensure helper references: API_URL, fetchWithTimeout, runTest,
evmPair, crossChainPair, and TEST_PAIRS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/public-api/src/index.ts`:
- Around line 48-59: Authentication was removed leaving v1Router endpoints
(v1Router.get('/swap/rates', getRates), v1Router.post('/swap/quote', getQuote),
v1Router.get('/chains', getChains), getChainCount, getAssets, getAssetCount,
getAssetById) unprotected from abuse; add a lightweight rate-limiting middleware
(e.g., express-rate-limit) and apply it to the v1Router (or specifically to swap
endpoints) by creating a rateLimit instance with sensible defaults (windowMs,
max, and a standard handler) and use v1Router.use(rateLimit(...)) before the
route registrations so all listed handlers are throttled.

In `@packages/public-api/tests/smoke-tests.ts`:
- Around line 88-174: Extract the duplicated rates-test body into a helper like
checkRatesForPair(pair, testLabel) and call it from the two runTest invocations;
the helper should (1) construct URLSearchParams from pair.sellAssetId /
pair.buyAssetId / pair.sellAmountCryptoBaseUnit, (2) call
fetchWithTimeout(`${API_URL}/v1/swap/rates?${params}`, {}, 30000), (3) assert
res.ok and parse JSON, (4) validate Array.isArray(data.rates), filter validRates
by !r.error && r.buyAmountCryptoBaseUnit && r.buyAmountCryptoBaseUnit !== '0',
and (5) emit the same console.warn/console.log branches. Replace the duplicated
inline async bodies for evmPair and crossChainPair with results.push(await
runTest(`Rates: ${pair.name}`, false, async () => checkRatesForPair(pair,
pair.name))). Ensure helper references: API_URL, fetchWithTimeout, runTest,
evmPair, crossChainPair, and TEST_PAIRS.

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