Skip to content

feat: add remote URL support for input specifications with optional auth#363

Merged
jonaslagoni merged 2 commits intomainfrom
remote-url-support
Apr 26, 2026
Merged

feat: add remote URL support for input specifications with optional auth#363
jonaslagoni merged 2 commits intomainfrom
remote-url-support

Conversation

@jonaslagoni
Copy link
Copy Markdown
Contributor

Summary

Adds support for loading AsyncAPI, OpenAPI, and JSON Schema specs from http(s):// URLs in inputPath, with optional authentication (bearer / apiKey / custom headers). Cross-spec $refs on the same and different hosts are resolved through the same auth-aware HTTP path. Closes #359.

Type of Change

  • New feature (adds functionality)
  • Documentation update
  • Bug fix
  • Breaking change
  • Performance improvement
  • Refactoring (no functional changes)
  • Test improvements

Related Issues

Closes #359 — prerequisite for the EventCatalog integration that needs to fetch authenticated specs.

Changes Made

New source files

  • src/utils/inputSource.tsisRemoteUrl / getInputSourceType (canonical URL-detection helper; telemetry now re-exports from here).
  • src/utils/remoteFetch.tsfetchRemoteDocument(url, auth?, options?) using Node 22's global fetch, with AbortController-based timeout, redirect-follow, and typed errors.
  • src/utils/refResolvers.tscreateOpenapiRefParserResolver(auth, ctx) and createAsyncapiResolvers(auth, ctx) so cross-spec $refs flow through the same auth + debug-log + cross-host-warning pipeline.

Modified source

  • src/codegen/types.ts — adds zodInputAuth (discriminated union spread into all three input branches), InputAuthConfig, and RunGeneratorContext.inputAuth.
  • src/codegen/configurations.ts — URL passthrough (bypass path.resolve for http(s)://) and threads context.inputAuth from config.
  • src/codegen/errors.tscreateRemoteFetchError with status-specific help (401/403 → auth, 404 → URL, 429 → rate-limit, network → connectivity, timeout → timeout). createInputDocumentError no longer suggests "valid JSON file" for URL paths.
  • src/codegen/inputs/asyncapi/parser.ts — URL fetch + per-call Parser with __unstable.resolver resolvers when auth is present (singleton preserved for the no-auth/local-file path).
  • src/codegen/inputs/openapi/parser.ts — URL fetch + Content-Type-driven parse + $RefParser.dereference with our auth-aware http resolver. safeUrlResolver: false so internal/loopback hosts work.
  • src/codegen/inputs/jsonschema/parser.ts — URL fetch with Content-Type → URL extension → JSON-then-YAML fallback. loadJsonSchema and loadJsonSchemaDocument deduplicated.
  • src/commands/generate.ts — watch mode skips chokidar for URL inputs and logs a one-shot warning; --watchPath still works.
  • src/telemetry/anonymize.ts — re-exports getInputSourceType from the canonical helper (no telemetry semantics change).

Tests (TDD: written first, verified failing, then passing)

  • test/utils/inputSource.spec.ts — URL-detection truth table.
  • test/utils/remoteFetch.spec.ts — auth headers (bearer / apiKey / custom), status codes, AbortError → timeout help, network failures, redirect-follow.
  • test/utils/refResolvers.spec.ts — both factories, debug log, cross-host info-level warning emitted exactly once per distinct cross-host destination.
  • test/codegen/configurations.spec.ts — URL passthrough (no path.resolve), inputAuth threaded onto context, Zod accepts each auth shape, rejects invalid shapes.
  • test/codegen/inputs/asyncapi.spec.ts — new file: URL load + auth + 401 propagation.
  • test/codegen/inputs/openapi.spec.ts — extended with URL load + auth + Content-Type-driven JSON/YAML + extension fallback + 404.
  • test/codegen/inputs/jsonschema.test.ts — extended with URL load + auth + Content-Type/YAML + ambiguous-content-type fallback.
  • test/codegen/inputs/__helpers__/httpServer.ts — real local http.createServer test helper with header-checking middleware.
  • test/codegen/inputs/remote-url.e2e.spec.ts — 7-case end-to-end suite against real HTTP servers, including same-host $ref auth, cross-host warning, and 401 on root.

Documentation

  • docs/configurations.md — new "Remote URL inputs" section with examples for the three auth shapes, plus the canonical "Auth scope and security considerations" section (compromised-spec example, debug/info log behavior, deferred mitigations list).
  • docs/inputs/asyncapi.md, docs/inputs/openapi.md, docs/inputs/jsonschema.md — short callouts linking to the canonical security section.
  • JSON schemas regenerated; the security WARNING flows into markdownDescription and surfaces in IDE tooltips and the playground's Monaco autocomplete.

Dependencies

  • @types/node bumped to ^22 (matches engines.node).
  • @apidevtools/json-schema-ref-parser promoted from transitive (via @readme/openapi-parser) to direct dependency at ^14.2.0.
  • No new HTTP-client dependency (Node 22 global fetch).

Key Implementation Details

Auth scope (D8) — same headers go to every fetched URL

When auth is configured, the headers are sent to every URL the loader fetches — root + every $ref target, regardless of host. This is the right default for typical internal-SSO setups but means a compromised spec can exfiltrate the token via an external $ref. Mitigations shipped today:

  1. Per-URL debug log ([remote-fetch] GET <url>).
  2. Info-level warning when a $ref host differs from the root host: [remote-fetch] auth headers sent to '<host>' while resolving $ref from '<root-host>'.
  3. Schema-level warning in zodInputAuth.describe(...) so it shows in IDE tooltips.

Per-host auth maps and an auth-host allowlist are deferred to follow-up issues; documented inline.

Browser & playground

The browser path passes spec content directly with a virtual inputPath and never invokes fetchRemoteDocument, so URL fetching is correctly Node-only. Esbuild tree-shakes out the URL helpers from the bundle (verified: grep -c fetchRemoteDocument returns 0). The new zodInputAuth schema is bundled so the playground accepts the auth field, with the security warning surfacing in Monaco autocomplete via markdownDescription.

Testing

npm run prepare:pr

All steps green: build, format, lint:fix, test:update, runtime:typescript:generate.

Unit + integration tests

Test Suites: 49 passed, 49 total
Tests:       1 skipped, 625 passed, 626 total

Browser tests (test/browser/)

Test Suites: 4 passed
Tests:       60 passed

Browser bundle smoke test

Loaded dist/browser/codegen.browser.mjs in Node with browser-globals shims; generate() produced a TypeScript payload from an AsyncAPI 2.6 spec; parseConfig() round-tripped a config with auth: {type: 'bearer'}.

Docusaurus website build

SUCCESS (pre-existing broken-link warnings on the website are unrelated to this PR).

Runtime generate (local-path regression)

All four runtime configs regenerated successfully — regular: 19 files, request-reply: 16, openapi: 18, payload-types: 60.

Documentation

  • docs/configurations.md — Remote URLs + Auth scope + watch-mode notes.
  • docs/inputs/{asyncapi,openapi,jsonschema}.md — short callouts.
  • JSDoc on all new public functions.
  • JSON schema description carries the security WARNING (verified in schemas/configuration-schema-0-with-docs.json).

Breaking Changes

None. Every existing test using a local inputPath still passes unchanged. The new auth field is optional and ignored for local file paths.

Performance Impact

  • No-auth + local-file path: unchanged (the AsyncAPI Parser singleton is reused; OpenAPI keeps the existing @readme/openapi-parser.dereference flow for local files).
  • URL path: one fetch per document/$ref; default 30 s timeout; redirect-follow.
  • Browser bundle size: unchanged shape (URL helpers are tree-shaken out).

Checklist

  • Code follows project conventions (Conventional Commits, no Claude attribution)
  • All tests pass (unit, blackbox via prepare:pr, browser, runtime generate sanity)
  • Documentation updated
  • No TypeScript errors (source + tests)
  • Lint passes (--max-warnings 0)
  • Build succeeds (CLI + browser bundle)
  • npm run prepare:pr green
  • Follows existing patterns
  • Backward compatible with local-file inputs

Examples

Bearer token

export default {
  inputType: 'asyncapi',
  inputPath: 'https://api.example.com/specs/asyncapi.yaml',
  language: 'typescript',
  auth: { type: 'bearer', token: process.env.API_TOKEN },
  generators: [
    { preset: 'payloads', outputPath: './src/payloads' }
  ]
};

API key in a custom header

auth: { type: 'apiKey', header: 'X-API-Key', value: process.env.API_KEY }

Arbitrary headers

auth: {
  type: 'custom',
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`,
    'X-Tenant': 'acme'
  }
}

Follow-ups (deferred per plan, not blocking)

  • Per-host auth maps: auth: { 'api.acme.com': {...}, 'schemas.acme.com': {...} }.
  • Auth-host allowlist: authHosts: [...] to scope headers to listed hosts only.
  • Disabling external $ref resolution entirely.

inputPath now accepts http(s):// URLs across AsyncAPI, OpenAPI, and JSON Schema
inputs, with an optional auth field (bearer / apiKey / custom headers) that flows
through to every fetch (root document and external $ref targets).

- Cross-spec $ref auth is supported for AsyncAPI (via @asyncapi/parser
  __unstable.resolver) and OpenAPI (via @apidevtools/json-schema-ref-parser
  custom http resolver).
- URL detection bypasses path.resolve at the configuration chokepoint.
- Watch mode skips chokidar for URL inputs and warns; --watchPath still works.
- Status-specific error help text (401/403/404/429/timeout/network).
- Cross-host info-level warning emitted once per distinct cross-host destination
  to surface potential token-leak vectors.
- Documentation: canonical 'Auth scope and security considerations' section in
  docs/configurations.md with the compromised-spec example and deferred-mitigations
  list; short callouts in docs/inputs/{asyncapi,openapi,jsonschema}.md.
- Browser bundle rebuilt; the new auth schema field surfaces in the playground's
  Monaco autocomplete with the security warning in markdownDescription.

Closes #359
@jonaslagoni jonaslagoni requested a review from ALagoni97 as a code owner April 26, 2026 18:25
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 26, 2026

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

Project Deployment Actions Updated (UTC)
the-codegen-project Ready Ready Preview, Comment Apr 26, 2026 6:36pm
the-codegen-project-mcp Ready Ready Preview, Comment Apr 26, 2026 6:36pm

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 26, 2026

Deploy Preview for the-codegen-project canceled.

Name Link
🔨 Latest commit 541d5ee
🔍 Latest deploy log https://app.netlify.com/projects/the-codegen-project/deploys/69ee5adb1035580008dc0100

@jonaslagoni jonaslagoni merged commit c39ea40 into main Apr 26, 2026
20 checks passed
@jonaslagoni jonaslagoni deleted the remote-url-support branch April 26, 2026 18:37
@jonaslagoni
Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 0.72.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add remote URL support for input specifications with authentication

1 participant