Repository navigation
Authenticate Entra ID marketplace API requests via RFC 9728 PRM negotiation - #325804
Karthik Nadig (karthiknadig) merged 7 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Implements authenticated access to Private Marketplaces by discovering RFC 9728 Protected Resource Metadata (PRM) and negotiating a resource-scoped bearer token, then threading that token through all VS Code surfaces that perform marketplace requests (workbench, shared process/remote, gallery API, and extension resource loading).
Changes:
- Add PRM discovery + resource-scoped token negotiation for auth-gated marketplace service indexes, exposing the negotiated token via
IExtensionGalleryManifestService.getAccessToken(). - Attach the negotiated token to marketplace API/asset requests and extension resource requests, guarded by same-secure-origin checks to prevent token leakage.
- Extend marketplace status modeling + UX (new statuses, provider-aware sign-in) and add unit tests covering negotiation/error/cache paths.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/services/extensionManagement/test/electron-browser/extensionGalleryManifestService.test.ts | Adds unit coverage for Microsoft/GitHub routing, negotiation (401→PRM), caching, and status transitions. |
| src/vs/workbench/services/extensionManagement/electron-browser/extensionGalleryManifestService.ts | Implements negotiation, caching, eligibility checks, token threading to IPC channels, and new status handling. |
| src/vs/workbench/contrib/extensions/common/extensions.ts | Re-exports the marketplace auth-provider context key for workbench contributions. |
| src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts | Updates welcome content and badges for new marketplace statuses and provider-specific sign-in messaging. |
| src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts | Registers extensions.gallery.authProvider setting + policy, and updates the marketplace sign-in action to support Microsoft/PRM consent. |
| src/vs/platform/extensionResourceLoader/common/extensionResourceLoaderService.ts | Passes resource URI into header computation for authenticated marketplace resource requests. |
| src/vs/platform/extensionResourceLoader/common/extensionResourceLoader.ts | Adds guarded Authorization header attachment for extension resource (README/etc.) fetches. |
| src/vs/platform/extensionResourceLoader/browser/extensionResourceLoaderService.ts | Mirrors resource-aware header computation for browser fetch paths. |
| src/vs/platform/extensionManagement/common/extensionGalleryService.ts | Attaches negotiated bearer token to extensionquery, stats/control, and asset download requests (same-secure-origin guarded). |
| src/vs/platform/extensionManagement/common/extensionGalleryManifestServiceIpc.ts | Threads negotiated access token over the manifest IPC channel and exposes getAccessToken() in non-window processes. |
| src/vs/platform/extensionManagement/common/extensionGalleryManifestService.ts | Adds a default getAccessToken() implementation returning undefined for open marketplaces. |
| src/vs/platform/extensionManagement/common/extensionGalleryManifest.ts | Introduces provider context key, new statuses, new config key/scopes, and PRM discovery helper/types. |
| src/vs/base/common/product.ts | Adds enableExtensionGalleryEntraAuth product gate for Microsoft/Entra marketplace auth path. |
| product.json | Extends product data to include a microsoft entry under the relevant auth access structure. |
| build/lib/policies/policyData.jsonc | Updates generated policy catalog to include ExtensionGalleryAuthProvider. |
Review details
- Files reviewed: 15/15 changed files
- Comments generated: 1
- Review effort level: Low
d708ccf to
4438731
Compare
📬 CODENOTIFYThe following users are being notified based on files changed in this PR: Robo (@deepak1556)Matched files:
|
1dc6076 to
aa13d37
Compare
1009737 to
832c974
Compare
Implements the GitHub provider's marketplace token acquisition on top of the account-service architecture from microsoft#325804: when the service index is gated, discover the protected resource (RFC 9728), then exchange the default account's GitHub session token at the marketplace's authorization server (RFC 8693). The subject token is only sent to a same-origin HTTPS token endpoint, with redirects disabled. Also validates the RFC 8414 issuer, strips Authorization on cross-origin redirects in the node request service, and re-validates on GitHub session changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b972512b-5cf0-4fb8-88b3-ef6b24a19441
53cd376 to
65d1118
Compare
Discover RFC 9728 metadata and negotiate resource-scoped Entra tokens for protected marketplaces. Keep account selection, consent, and authorization lifecycle in the Account Service, with a shared read-only platform view and origin-restricted process replicas. Simplify access negotiation and cover authorization cleanup, process identity, and request routing with regression tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3c5458c9-6cfb-4157-bdad-76383ffab22b
ef92e3f to
bbc135b
Compare
|
Sandeep Somavarapu (@sandy081) this is ready for your re-review |
d71149f
into
microsoft:main
Post-merge implementation record and follow-upsThe description above predates the final review changes. This note corrects the record for the implementation merged as d71149f; it does not imply the follow-up fixes below have already landed.
The post-merge feedback is addressed in separate, ready-for-review upstream PRs to keep reviews focused:
Transport clarification: the remote server uses the Node request implementation, while the shared process supplies Electron The GitHub exchange feature in #325815 remains in review. Its existing Node hardening and GitHub-only renewal are not fixes already deployed on main, nor do they supply Entra renewal. Rebase that feature after the standalone transport change lands rather than duplicating its older redirect implementation. The first three fixes above are the deployment priorities. Explicit security sign-off on the remote credential-delegation boundary has been requested from TylerLeonhardt in #343689 (comment). Approval is still pending; source checks and regressions do not grant it. Drafted with GitHub Copilot. |
Builds on #325331 (now merged), which added Microsoft Entra ID sign-in as a licensing signal for a Private Marketplace but left the token it produced unused. Partially addresses #325412.
Why
A Private Marketplace running with authentication enforced refuses its service index outright unless a token minted for the marketplace itself is presented. Signing in is not enough: the sign-in token identifies the user, but its audience is not the marketplace.
The marketplace's resource identity cannot be shipped in
product.json, because a self-hosted deployment has its own app registration and its own scopes — one value baked into the build cannot serve every enterprise. So the client has to learn it from the resource at runtime, which is exactly what RFC 9728 exists for.What it does
/.well-known/oauth-protected-resourceto learn its authorization server and scopes.extensionquery/search, control manifest, asset and VSIX downloads, extension resources (icons, README incl. embedded images, CHANGELOG), and the shared-processgetManifest/VSIX path.Whether a marketplace needs any of this is discovered, not configured: one that accepts what it is given is never asked what a token for it should look like, and never has the well-known endpoint fetched.
Discovery uses the well-known endpoint rather than the
WWW-Authenticatechallenge, because that header is not CORS-safelisted and the renderer's cross-origin index fetch usually cannot read it. The challenge is used only as a hint for an explicitresource_metadataURL.Which origins may receive the bearer
IExtensionGalleryManifestService.getAccessToken()is replaced by:Callers ask for headers instead of handling a raw token, so the rule deciding which origins may receive the bearer has one implementation, in the platform base class.
The rule is strict same-origin over
httpsagainst the service index — the endpoint that demanded the token and that it was minted for — and it fails closed. This matters concretely: a marketplace serves assets from its own origin when proxying, but upstreamed extensions are fetched from the public Microsoft marketplace, which must never receive a private marketplace's Entra token. A parent-domain suffix match would be unsafe here, since these deployments can share a domain with unrelated tenants.The bearer is dropped whenever the marketplace is retracted, so a sign-out, account switch, or configuration change cannot leave a usable one behind.
Commits
Behavior notes for reviewers
RequiresSignIn, notAccessDenied.AccessDeniedrenders as "contact your administrator" with no action; a missing consent grant is resolvable by the user, so sending them there was a dead end.Testing
extensionGalleryManifestServiceIpc.test.tscovers the cross-process channel, which is the one path where an argument-order mistake would silently leak or drop the bearer.