Skip to content

feat: open gated model access pages in the Comfy session#1275

Merged
jaeone94 merged 7 commits into
mainfrom
jaeone/desk2-126-gated-hf-model-access-bridge
Jul 20, 2026
Merged

feat: open gated model access pages in the Comfy session#1275
jaeone94 merged 7 commits into
mainfrom
jaeone/desk2-126-gated-hf-model-access-bridge

Conversation

@jaeone94

@jaeone94 jaeone94 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

This is the Desktop half of the gated Hugging Face model access flow introduced in Comfy-Org/ComfyUI_frontend#13742.

The frontend PR adds a lock action beside missing models that appear to require Hugging Face authorization while leaving the normal Download action intact. This paired PR adds an explicit openModelAccessPage() Desktop bridge implementation so that action opens the repository in a Hugging Face-only Desktop window using the same Electron session as the originating ComfyUI view.

Sharing the session is the important part: after the user signs in and accepts the repository terms, the existing Desktop model download manager uses that same session for session.downloadURL(). The user can return to ComfyUI, click Download again, and complete the download without a separate OAuth or token flow.

Changes

  • Expose openModelAccessPage(url) from the ComfyUI preload bridge.
  • Register a dedicated desktop2-open-model-access-page IPC handler.
  • Resolve the host window and Electron session from the IPC sender rather than using a global browser session.
  • Open the repository in a child BrowserWindow configured with the sender's session, no preload, nodeIntegration: false, contextIsolation: true, and sandbox: true.
  • Accept only exact https://huggingface.co/<owner>/<repository> model repository URLs on the default HTTPS port as bridge entry points.
  • Allow navigation anywhere within the exact https://huggingface.co origin after the trusted repository window opens. This keeps Hugging Face home, search, settings, sign-in, sign-out, license, Spaces, and other repository flows usable.
  • Block HTTP, lookalike hosts, subdomains, cross-origin navigation, new Electron popups, and browser-permission requests from the remote access page.
  • Reuse an existing window only for the same Comfy sender and repository. Separate repositories and separate sender sessions cannot overwrite or share an access flow.
  • Destroy access windows with their owning Comfy host and remove lifecycle listeners when a child closes.
  • Capture the access page webContents while the window is alive so the closed handler can finish cleanup without dereferencing a destroyed BrowserWindow.
  • Treat a superseded ERR_ABORTED navigation as accepted, while returning false for genuine initial load failures so the frontend can run its browser fallback.
  • Add focused tests for the preload/IPC contract, URL and origin boundaries, session ownership, lifecycle cleanup, load failures, popup denial, and permission handling.

Paired Frontend Contract

The frontend contract is defined by Comfy-Org/ComfyUI_frontend#13742 as an optional bridge method:

openModelAccessPage?: (url: string) => Promise<boolean>

The method resolves true when Desktop takes ownership of the trusted access-page request. It resolves false when the URL is not trusted, the originating ComfyUI host is unavailable, or the initial page load genuinely fails. A false result allows the frontend to fall back to opening the page in the normal browser.

Desktop currently consumes the published bridge-types package at 0.1.2, while the frontend PR advances that package to 0.1.4. The preload uses a small local intersection type so both PRs can be reviewed and tested in parallel. Once 0.1.4 is published, Desktop can bump the package and remove that temporary local type without changing runtime behavior.

User Flow

  1. The user clicks Download for a gated model and the Desktop download fails because the session does not yet have access.
  2. The user clicks the lock icon added by the paired frontend PR.
  3. Desktop opens the actual Hugging Face repository in a window backed by the same Electron session as ComfyUI.
  4. The user signs in with Hugging Face and accepts the repository's access terms.
  5. The user returns to ComfyUI and clicks Download again.
  6. The existing Desktop download manager receives the authorized session cookies and downloads the model normally.

Intended Scope and Limitations

  • Desktop does not independently classify a model as gated; it consumes the repository URL hint produced by the frontend.
  • The bridge remains a narrow model-repository entry point, not an arbitrary URL opener. Once opened, the hardened remote window acts as a constrained first-party Hugging Face browser and permits navigation only within the exact Hugging Face origin.
  • Third-party identity-provider redirects and popup-based enterprise SSO are not enabled. Cross-origin navigation and all renderer-created windows remain blocked.
  • This does not add Hugging Face OAuth, token storage, provider API calls, model discovery, or new download-manager states.
  • The access window does not automatically retry a failed model download. The user returns to ComfyUI and clicks Download again.
  • The feature depends on the paired frontend UI; older frontends simply do not call the optional bridge method.

Review Focus

  • Confirm that both the access window and startModelDownload() use the originating ComfyUI sender session.
  • Confirm that bridge entry validation happens before BrowserWindow creation and cannot be bypassed with HTTP, a lookalike hostname, a subdomain, a reserved route, or a non-repository path.
  • Confirm that post-entry navigation is allowed throughout the exact https://huggingface.co origin while all other origins remain blocked.
  • Confirm that the remote page receives no Desktop preload, Node.js capability, popup surface, or browser permissions.
  • Confirm that access windows cannot outlive their host or cross sender-session boundaries.
  • Confirm that genuine load failures return false while superseded navigation remains open.
  • Confirm that this remains a provider-access bridge rather than a second model download implementation.

Validation

  • pnpm run typecheck
  • pnpm run lint
  • pnpm run build
  • pnpm run test (190 files, 2,778 passed, 1 skipped)
  • pnpm run test:integration (25 passed)
  • pnpm exec vitest run src/main/lib/modelAccessPage.test.ts (25 passed, including destroyed-window cleanup)
  • Manually verified the normal Hugging Face access-grant and retry flow using the paired frontend production build in Comfy Desktop.
  • Manually verified Hugging Face sign-out and same-origin navigation in the embedded access window after replacing the path allowlist with an exact-origin boundary.
  • Reproduced an immediate login navigation as ERR_ABORTED (-3) in Electron and verified that the access window reaches the login page while the main process remains responsive.

An automated E2E test is not included because this flow requires a real third-party Hugging Face account, a gated repository access grant, and persistent cookies in a running Electron session. Unit tests cover the deterministic bridge, trust boundary, lifecycle, and session wiring; the authenticated provider flow was verified manually.

Screenshots

2026-07-17.2.52.20.mov

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The change adds a validated Hugging Face model access window, connects it through a new IPC handler and preload bridge method, and tests URL validation, session wiring, security controls, lifecycle behavior, loading, and rejection paths.

Model access page

Layer / File(s) Summary
Validated model access window
src/main/lib/modelAccessPage.ts, src/main/lib/modelAccessPage.test.ts
isModelAccessPageUrl restricts URLs to HTTPS huggingface.co model paths without ports. openModelAccessPageWindow creates or reuses a hidden session-linked window, handles loading and parent lifecycle events, and covers success and rejection paths.
Embedded page security controls
src/main/lib/modelAccessPage.ts, src/main/lib/modelAccessPage.test.ts
Navigation and redirect guards allow only validated model URLs, new Electron windows are denied, and session permission handlers enforce the embedded-page rules.
IPC and preload wiring
src/main/lib/ipc/registerDownloadHandlers.ts, src/main/lib/ipc/registerDownloadHandlers.test.ts, src/preload/comfyPreload.ts, src/preload/comfyPreload.test.ts
Registers desktop2-open-model-access-page and exposes openModelAccessPage(url) through the preload bridge, with tests covering IPC forwarding and bridge invocation.

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant comfyPreload
  participant registerDownloadHandlers
  participant openModelAccessPageWindow
  participant BrowserWindow
  Renderer->>comfyPreload: openModelAccessPage(url)
  comfyPreload->>registerDownloadHandlers: invoke desktop2-open-model-access-page
  registerDownloadHandlers->>openModelAccessPageWindow: pass sender and url
  openModelAccessPageWindow->>BrowserWindow: create or reuse session-linked window
  openModelAccessPageWindow->>BrowserWindow: loadURL(url)
Loading

Suggested reviewers: kosinkadink

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jaeone/desk2-126-gated-hf-model-access-bridge
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch jaeone/desk2-126-gated-hf-model-access-bridge

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/lib/modelAccessPage.ts`:
- Around line 22-53: Update openModelAccessPageWindow to track the active access
BrowserWindow in a shared activeAccessWindow reference; if it exists and is not
destroyed, focus it and return without creating another window. Clear the
reference when the window closes or fails to load, while preserving the existing
creation and URL validation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fc8e6d05-9a8c-485b-ac62-f242760756cb

📥 Commits

Reviewing files that changed from the base of the PR and between ebaa998 and d930da9.

📒 Files selected for processing (4)
  • src/main/lib/ipc/registerDownloadHandlers.ts
  • src/main/lib/modelAccessPage.test.ts
  • src/main/lib/modelAccessPage.ts
  • src/preload/comfyPreload.ts

Comment thread src/main/lib/modelAccessPage.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/lib/modelAccessPage.ts`:
- Around line 27-30: Update modelRepositoryPath to allow only valid Hugging Face
model repository routes, rejecting reserved namespaces such as datasets, login,
and settings before returning a trusted path. Preserve the existing
owner/repository validation, and add table-driven tests covering accepted model
paths and rejected reserved or non-model routes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ac96aa32-d8fd-47c2-9ce9-092b772a47d8

📥 Commits

Reviewing files that changed from the base of the PR and between d930da9 and ac2626f.

📒 Files selected for processing (4)
  • src/main/lib/ipc/registerDownloadHandlers.test.ts
  • src/main/lib/modelAccessPage.test.ts
  • src/main/lib/modelAccessPage.ts
  • src/preload/comfyPreload.test.ts

Comment thread src/main/lib/modelAccessPage.ts
benceruleanlu
benceruleanlu previously approved these changes Jul 20, 2026
@benceruleanlu

Copy link
Copy Markdown
Member

Hold on a second actually

@benceruleanlu
benceruleanlu dismissed their stale review July 20, 2026 14:44

New finding in happy path that I would actually say is blocking

@benceruleanlu benceruleanlu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Below verified manually

Bug: closing the Hugging Face window throws TypeError: Object has been destroyed in the main process

The closed handler's first line accesses accessWindow.webContents, but the window is already destroyed when closed fires — verified on Electron 40.4.1 for both close() and destroy(). Every close path hits it, including the happy path (sign in, accept terms, close the window).

The app only registers uncaughtExceptionMonitor, so the exception goes unhandled and packaged builds show Electron's native "A JavaScript error occurred in the main process" dialog on every close. The throw also skips the rest of the handler (stale accessWindows entry, leaked parent closed listener). Tests miss it because the mocked webContents never throws after destroy.

Fix — capture the reference while the window is alive:

const accessContents = accessWindow.webContents
accessWindow.once('closed', () => {
  accessPageContents.delete(accessContents)
  ...
})

@jaeone94

Copy link
Copy Markdown
Contributor Author

@benceruleanlu Thanks for catching and manually verifying this. Fixed in 55c1cc8: the access-page webContents reference is now captured while the window is alive, so the closed handler no longer dereferences a destroyed BrowserWindow and the remaining map/listener cleanup can complete. I also added a regression test that makes post-destruction webContents access throw Object has been destroyed and verifies the close handler still finishes. The focused suite now passes 25/25, and the full typecheck and lint gates pass.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/lib/modelAccessPage.test.ts`:
- Line 179: Update the test assertion around accessWindowListeners.get('closed')
to remove optional chaining, so the test invokes the required closed listener
directly and fails when it is missing rather than silently skipping the
assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4ecca556-c1a1-4cec-94f5-b70ab083f606

📥 Commits

Reviewing files that changed from the base of the PR and between 6a766da and 55c1cc8.

📒 Files selected for processing (2)
  • src/main/lib/modelAccessPage.test.ts
  • src/main/lib/modelAccessPage.ts

Comment thread src/main/lib/modelAccessPage.test.ts

@benceruleanlu benceruleanlu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nothing blocking

@jaeone94
jaeone94 removed the request for review from Kosinkadink July 20, 2026 17:52
@jaeone94
jaeone94 merged commit fed483d into main Jul 20, 2026
12 checks passed
@jaeone94
jaeone94 deleted the jaeone/desk2-126-gated-hf-model-access-bridge branch July 20, 2026 17:59
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants