Skip to content

feat(runtime): support auth headers and additional fetch options when loading remote modules - #4827

Open
smeng9 wants to merge 35 commits into
module-federation:mainfrom
smeng9:feat/remote-fetch-options
Open

feat(runtime): support auth headers and additional fetch options when loading remote modules#4827
smeng9 wants to merge 35 commits into
module-federation:mainfrom
smeng9:feat/remote-fetch-options

Conversation

@smeng9

@smeng9 smeng9 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Description

A compliance requirement in the backend gateway needs an Authorization: Bearer … header (plus arbitrary X-* headers) for loading remote modules. Previously all assets that are fetched with no authorization header will be rejected under new access control model.

This PR adds per-remote fetchOptions so federated ESM/module remotes can be loaded with custom HTTP headers on their manifest, remote entry, split chunks, shared deps, and CSS requests.

  mf.registerRemotes(
    [{ name, entry, type: 'module' }],
    { fetchOptions: { headers: { Authorization: `Bearer ${token}` } } },
  );

Related Issue

Multiple people had requested this feature before
module-federation/utilities#13
module-federation/enhanced#21

Additionally previous discussions are outdated,
they involve webpack require() instead of esm import()
#1115

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4c46bb4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 47 packages
Name Type
@module-federation/runtime-core Major
@module-federation/nextjs-mf Patch
@module-federation/runtime Major
@module-federation/bridge-react Major
@module-federation/devtools Major
@module-federation/dts-plugin Major
@module-federation/esbuild Patch
@module-federation/metro Major
@module-federation/modern-js-v3 Major
@module-federation/modern-js Major
@module-federation/node Patch
@module-federation/observability-plugin Patch
@module-federation/playground Patch
@module-federation/retry-plugin Major
@module-federation/runtime-tools Major
@module-federation/webpack-bundler-runtime Major
@module-federation/bridge-vue3 Major
website-new Patch
@module-federation/metro-plugin-rnc-cli Major
@module-federation/metro-plugin-rnef Major
@module-federation/metro-plugin-rock Major
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
@module-federation/rsbuild-plugin Major
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
@module-federation/enhanced Major
@module-federation/rspack Major
@module-federation/inject-external-runtime-core-plugin Major
@module-federation/rspress-plugin Major
remote5 Patch
remote6 Patch
@module-federation/storybook-addon Major
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
@module-federation/sdk Major
@module-federation/managers Major
@module-federation/manifest Major
@module-federation/third-party-dts-extractor Major
@module-federation/bridge-shared Major
@module-federation/bridge-react-webpack-plugin Major
@module-federation/error-codes Major
create-module-federation Major
@module-federation/cli Major
@module-federation/treeshake-server Major
@module-federation/treeshake-frontend Major
@module-federation/utilities Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jun 17, 2026

Copy link
Copy Markdown

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit 6b4c56a
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/6a4cad8b74064600085358bb
😎 Deploy Preview https://deploy-preview-4827--module-federation-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3686009e9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/runtime-core/src/utils/load.ts Outdated
Comment thread packages/runtime-core/src/utils/preload.ts Outdated
Comment thread packages/sdk/src/blobLoad.ts Outdated
@smeng9
smeng9 force-pushed the feat/remote-fetch-options branch 3 times, most recently from 00527f7 to 9195060 Compare June 17, 2026 21:30
@2heal1

2heal1 commented Jun 22, 2026

Copy link
Copy Markdown
Member

The ESM loading direction makes sense, but I don't think fetchOptions should be added to registerRemote/registerRemotes.

The runtime already has a fetch hook, and manifest loading already uses that hook with remoteInfo. Header/auth customization is request behavior, so keeping it in the existing fetch lifecycle is more consistent than adding a second configuration path on remote registration.

This also avoids expanding the public remote config API. Users can already decide headers based on remoteInfo in the hook, which also works better for dynamic cases such as token refresh or per-request auth logic.

So I think the change should keep remote registration unchanged, and make ESM remote entry / related ESM asset loading go through the existing fetch hook with remoteInfo.

@smeng9

smeng9 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Hi @2heal1

I have applied your suggestions to keep the function signature the same for registerRemotes.

Would you mind take another look? Thanks!

@pkg-pr-new

pkg-pr-new Bot commented Jul 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/@module-federation/devtools@a2fbd96

@module-federation/cli

pnpm add https://pkg.pr.new/@module-federation/cli@a2fbd96

create-module-federation

pnpm add https://pkg.pr.new/create-module-federation@a2fbd96

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/@module-federation/dts-plugin@a2fbd96

@module-federation/enhanced

pnpm add https://pkg.pr.new/@module-federation/enhanced@a2fbd96

@module-federation/error-codes

pnpm add https://pkg.pr.new/@module-federation/error-codes@a2fbd96

@module-federation/esbuild

pnpm add https://pkg.pr.new/@module-federation/esbuild@a2fbd96

@module-federation/managers

pnpm add https://pkg.pr.new/@module-federation/managers@a2fbd96

@module-federation/manifest

pnpm add https://pkg.pr.new/@module-federation/manifest@a2fbd96

@module-federation/metro

pnpm add https://pkg.pr.new/@module-federation/metro@a2fbd96

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnc-cli@a2fbd96

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnef@a2fbd96

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rock@a2fbd96

@module-federation/modern-js

pnpm add https://pkg.pr.new/@module-federation/modern-js@a2fbd96

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/@module-federation/modern-js-v3@a2fbd96

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/@module-federation/native-federation-tests@a2fbd96

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/@module-federation/native-federation-typescript@a2fbd96

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/@module-federation/nextjs-mf@a2fbd96

@module-federation/node

pnpm add https://pkg.pr.new/@module-federation/node@a2fbd96

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/@module-federation/observability-plugin@a2fbd96

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/@module-federation/retry-plugin@a2fbd96

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/@module-federation/rsbuild-plugin@a2fbd96

@module-federation/rspack

pnpm add https://pkg.pr.new/@module-federation/rspack@a2fbd96

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/@module-federation/rspress-plugin@a2fbd96

@module-federation/runtime

pnpm add https://pkg.pr.new/@module-federation/runtime@a2fbd96

@module-federation/runtime-core

pnpm add https://pkg.pr.new/@module-federation/runtime-core@a2fbd96

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/@module-federation/runtime-tools@a2fbd96

@module-federation/sdk

pnpm add https://pkg.pr.new/@module-federation/sdk@a2fbd96

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/@module-federation/storybook-addon@a2fbd96

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/@module-federation/third-party-dts-extractor@a2fbd96

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/@module-federation/treeshake-frontend@a2fbd96

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/@module-federation/treeshake-server@a2fbd96

@module-federation/typescript

pnpm add https://pkg.pr.new/@module-federation/typescript@a2fbd96

@module-federation/utilities

pnpm add https://pkg.pr.new/@module-federation/utilities@a2fbd96

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/@module-federation/webpack-bundler-runtime@a2fbd96

@module-federation/bridge-react

pnpm add https://pkg.pr.new/@module-federation/bridge-react@a2fbd96

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/@module-federation/bridge-react-webpack-plugin@a2fbd96

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/@module-federation/bridge-shared@a2fbd96

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/@module-federation/bridge-vue3@a2fbd96

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/@module-federation/inject-external-runtime-core-plugin@a2fbd96

commit: a2fbd96

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Report

18 package(s) changed, 23 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/bridge-react 387.1 kB +10.5 kB (+2.8%) 1.3 kB -1 B (-0.1%)
@module-federation/bridge-vue3 178.6 kB +7.0 kB (+4.1%) 26.2 kB +1.3 kB (+5.2%)
@module-federation/cli 26.3 kB no change 786 B no change
@module-federation/core 39.1 kB no change 173 B no change
@module-federation/devtools 664.0 kB no change 4.0 kB no change
@module-federation/dts-plugin 313.1 kB +12 B (+0.0%) 4.7 kB +1 B (+0.0%)
@module-federation/enhanced 809.4 kB +223 B (+0.0%) 672 B no change
@module-federation/managers 69.8 kB no change 334 B no change
@module-federation/manifest 136.1 kB no change 182 B no change
@module-federation/metro-plugin-rnc-cli 0 B no change 314 B no change
@module-federation/modern-js-v3 185.7 kB +31 B (+0.0%) 875 B no change
@module-federation/node 193.4 kB no change 217 B no change
@module-federation/retry-plugin 67.3 kB +1.5 kB (+2.2%) 2.8 kB +36 B (+1.3%)
@module-federation/runtime 20.0 kB -13 B (-0.1%) 715 B no change
@module-federation/runtime-core 289.4 kB +2.1 kB (+0.7%) 477 B no change
@module-federation/sdk 132.6 kB +11.3 kB (+9.3%) 864 B +79 B (+10.1%)
@module-federation/storybook-addon 79.1 kB no change 100 B no change
@module-federation/utilities 110.6 kB no change 328 B no change

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/bridge-react 18.8 kB +980 B (+5.3%) 19.2 kB +878 B (+4.7%)
@module-federation/bridge-vue3 19.9 kB +1005 B (+5.2%) 19.5 kB +882 B (+4.6%)
@module-federation/cli 2.3 kB -30 B (-1.3%) 2.3 kB -30 B (-1.3%)
@module-federation/core 1.1 kB -29 B (-2.6%) 1.0 kB -28 B (-2.6%)
@module-federation/devtools 30.2 kB -24 B (-0.1%) 30.2 kB -24 B (-0.1%)
@module-federation/dts-plugin 14.4 kB +3 B (+0.0%) 14.4 kB +3 B (+0.0%)
@module-federation/enhanced 2.6 kB -42 B (-1.5%) 2.6 kB -42 B (-1.5%)
@module-federation/managers 2.4 kB -24 B (-1.0%) 2.4 kB -24 B (-1.0%)
@module-federation/manifest 6.1 kB -37 B (-0.6%) 6.1 kB -37 B (-0.6%)
@module-federation/metro-plugin-rnc-cli 411 B -23 B (-5.3%) 411 B -23 B (-5.3%)
@module-federation/modern-js-v3 4.7 kB -4 B (-0.1%) 4.7 kB -4 B (-0.1%)
@module-federation/node 9.2 kB -26 B (-0.3%) 9.2 kB -26 B (-0.3%)
@module-federation/retry-plugin 1.9 kB +22 B (+1.2%) 1.9 kB +22 B (+1.2%)
@module-federation/runtime 687 B no change 687 B no change
@module-federation/runtime-core 15.4 kB +170 B (+1.1%) 15.1 kB +120 B (+0.8%)
@module-federation/sdk 5.5 kB +971 B (+21.1%) 6.3 kB +927 B (+16.9%)
@module-federation/storybook-addon 1.9 kB -23 B (-1.2%) 1.7 kB -22 B (-1.2%)
@module-federation/utilities 2.6 kB -29 B (-1.1%) 2.6 kB -29 B (-1.1%)

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 22.5 kB +1.1 kB (+5.1%) 23.5 kB +1003 B (+4.4%) +990 B -120 B

Total dist (raw): 6.85 MB (+32.6 kB (+0.5%))
Total ESM gzip: 79.9 kB (+1.4 kB (+1.8%))
Total web bundle (gzip): 210.1 kB (+2.8 kB (+1.3%))
Total node bundle (gzip): 210.3 kB (+2.5 kB (+1.2%))
Tracked ./bundler entry gzip: 556 B (no change)
Tracked ./bundler web bundle (gzip): 4.8 kB (no change)
Tracked ./bundler node bundle (gzip): 4.8 kB (no change)

Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as ./bundler are measured separately so ENV_TARGET-driven tree-shaking is visible. Bare imports are externalized to keep package-level sizes consistent, and assets are emitted as resources.

@smeng9

smeng9 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Hi @2heal1

I have rebased my tests to use newly merged rstest

@smeng9
smeng9 force-pushed the feat/remote-fetch-options branch from 6b4c56a to 6061b9b Compare July 10, 2026 18:27
@smeng9

smeng9 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Hi @2heal1

I have rebased with recent merges, including the added new language.

Could you please help us to close the compliance gap? Thanks!

@smeng9
smeng9 force-pushed the feat/remote-fetch-options branch from 6061b9b to bda4fa8 Compare July 28, 2026 18:00
Shaoyu Meng and others added 15 commits August 4, 2026 16:36
Design for threading per-remote fetchOptions through registerRemotes so
auth/compliance headers reach every asset in the ESM module graph
(manifest, entry, chunks, shared deps, manifest CSS) via a fetch + blob
import-rewriting loader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…motes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… loading

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Distinguishes the JS module blob cache from cssCache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dom.iterable surfaces Headers.entries()/Symbol.iterator on the standard
DOM types, so toHeaderObject can use Object.fromEntries(headers.entries())
without the unknown-cast workaround. Verified: all 42 packages build,
sdk type-checks, sdk tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shaoyu Meng and others added 19 commits August 4, 2026 16:36
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Call-level fetchOptions now act as defaults that each remote's own
fetchOptions are merged on top of (remote wins on conflict), with headers
merged per-key so a remote adding one header no longer drops the
call-level ones. Exposes toHeaderObject from sdk to share the
header-normalization used by the merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The entry preload in preloadAssets used the snapshot-derived moduleInfo, which
does not carry the host remote's fetchOptions. The remoteEntry was therefore
preloaded unauthenticated and cached, so the later authenticated load was
skipped and no Authorization header reached the remoteEntry or its chunks.
Forward remoteInfo.fetchOptions to the entry preload (mirroring the css path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…docs

Document the per-remote/call-level fetchOptions option on registerRemotes in
the runtime API reference (en + zh), and remove the internal superpowers
plan/spec scratch files under docs/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- runtime-core/load: wrap blob-loader ESM entry failures as RUNTIME_008 so
  getRemoteEntry's loadEntryError recovery (token refresh, failover) still
  fires for authenticated ESM remotes
- runtime-core/preload: skip applying authenticated CSS during preload hints
  (useLinkPreload) so a remote's stylesheet no longer overrides host styles
  before the remote is loaded
- sdk/blobLoad: store dynamic-import contexts in a shared globalThis registry
  and stop clobbering an existing __mfDyn shim, so blob modules keep their
  fetch context when two bundled copies of the SDK coexist

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instead of adding a separate __MF_BLOB_LOAD_CONTEXTS__ global, attach the
shared dynamic-import context map as a property of the single global __mfDyn
shim. Avoids polluting globalThis with a second variable while keeping the
cross-SDK-copy guarantee: every copy reads/writes the same map through
whichever shim is installed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getContexts() already installs the __mfDyn shim idempotently, so the only
thing the flag still guarded was duplicate vite:preloadError listeners. Use a
stable handler reference instead — addEventListener treats an identical
(callback, capture) pair as a no-op — and remove the flag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- __mfDyn -> __mfDynImport (global dynamic-import shim)
- MFDynShim -> MFDynImportShim
- getContexts -> createOrGetBlobLoaderContexts
- shim.contexts -> shim.blobLoaderContexts
- BlobLoadContext -> BlobLoaderContext

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract pushCssAsset so the three CSS branches (authenticated fetch, rel=preload
hint, applied rel=stylesheet) share a single forEach instead of repeating the
cssAssets.forEach/results.push boilerplate. Behavior is unchanged:
needDeleteLink stays undefined for the preload hint and false for the applied
stylesheet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…registerRemotes config

Address PR review (2heal1): header/auth customization is request behavior, so
configure it through the existing `fetch` hook (which already receives
`remoteInfo`) rather than a new `fetchOptions` field on remote registration.

- Remove `fetchOptions` from `RemoteInfoCommon`/`RemoteInfo`, the
  `registerRemotes` options, and the call-level merge helper; revert the
  manifest fetch to its prior `{}` init (it already passed `remoteInfo`).
- Gate the ESM remote-entry and manifest-CSS blob loaders on fetch-hook
  presence (`loaderHook.fetch.listeners.size > 0`) instead of `fetchOptions`,
  routing their requests through the hook with `remoteInfo`/`resourceContext`.
  With no fetch hook, remotes load via native import()/<link> as before.
- Rename/rewrite tests to cover the hook-presence gate; update EN/ZH docs to
  document the `fetch` hook approach and refresh the changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng behavior

The test only asserted that the manifest request is emitted through the fetch
hook with remoteInfo as the 3rd arg — behavior that predates this branch (it
was already in place at v2.5.0) and that the test never inspected the init arg
this branch actually touched. It guarded no code introduced here, so remove it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rs.spyOn/rs.mock on @module-federation/sdk never intercepts calls from
src/utils/load.ts and preload.ts: their named imports bind directly to
the sdk's inner dist modules, bypassing the mocked package entry. Follow
the sdk blobLoad.spec.ts idiom instead — run the real blob loader, stub
globalThis.fetch and URL.createObjectURL, and assert the observable
fetch/hook-emit behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@smeng9
smeng9 force-pushed the feat/remote-fetch-options branch 3 times, most recently from cafaa9d to 305e394 Compare August 5, 2026 00:13
The fetch + blob ESM loader lived in packages/sdk, which has no
@module-federation/error-codes dependency. That forced loadEntryDom to
wrap every call in a second function just to map loader failures onto
RUNTIME_008.

Move blobLoad.ts and its spec into runtime-core/src/utils so
loadEsmEntryWithFetch reports RUNTIME_008 itself, the way loadEntryScript
does. This drops the confusingly-named wrapper, the
Promise<RemoteEntryExports> cast, and the sdk's public export of what is
really a runtime-core internal.

The spec is converted from jest to rstest to match runtime-core's runner.
packages/sdk is now unchanged on this branch, so it no longer needs a
version bump in the changeset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@smeng9
smeng9 force-pushed the feat/remote-fetch-options branch from 305e394 to 4c46bb4 Compare August 5, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants