Skip to content

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

Open
smeng9 wants to merge 30 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 30 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.

Shaoyu Meng and others added 19 commits June 16, 2026 14:08
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>
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>
@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f9438b0

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

This PR includes changesets to release 46 packages
Name Type
@module-federation/runtime-core Major
@module-federation/sdk Major
@module-federation/nextjs-mf Patch
@module-federation/runtime Major
@module-federation/bridge-react Major
@module-federation/devtools Major
@module-federation/cli Major
@module-federation/dts-plugin Major
@module-federation/enhanced Major
@module-federation/esbuild Patch
@module-federation/managers Major
@module-federation/manifest Major
@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/retry-plugin Major
@module-federation/rsbuild-plugin Major
@module-federation/rspack Major
@module-federation/rspress-plugin Major
@module-federation/storybook-addon Major
@module-federation/utilities Patch
@module-federation/webpack-bundler-runtime Major
@module-federation/bridge-react-webpack-plugin Major
@module-federation/bridge-vue3 Major
@module-federation/runtime-tools Major
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider 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
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
remote5 Patch
remote6 Patch
website-new Patch
@module-federation/inject-external-runtime-core-plugin Major
@module-federation/third-party-dts-extractor Major
@module-federation/bridge-shared Major
@module-federation/error-codes Major
create-module-federation Major
@module-federation/treeshake-server Major
@module-federation/treeshake-frontend Major

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 f9438b0
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/6a3a0ee7a1af75000835c900
😎 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
Shaoyu Meng and others added 4 commits June 17, 2026 11:11
…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>
@smeng9 smeng9 force-pushed the feat/remote-fetch-options branch from 4a5e692 to 2f1fb20 Compare June 17, 2026 21:17
- __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>
@smeng9 smeng9 force-pushed the feat/remote-fetch-options branch from 2f1fb20 to 1adcded Compare June 17, 2026 21:17
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>
@smeng9 smeng9 force-pushed the feat/remote-fetch-options branch 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.

Shaoyu Meng and others added 5 commits June 22, 2026 17:40
…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>
@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!

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