-
Notifications
You must be signed in to change notification settings - Fork 41.9k
Enable Microsoft Entra ID sign-in for Private Marketplace access #325331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 37 commits
d97c8a8
6f54fb9
ab9e525
3481985
94a70a7
bd44656
eec93cb
6434607
ca0d56f
5510595
2f7a8f9
6ec632f
3aa3035
988ffeb
c0306bc
5ec451b
0b48532
b5aaf89
3789654
0630e80
322b2e3
67955a3
84ff6e0
fc4e4a0
117928b
d2626c5
af73002
c67ffce
cec950d
bbfd837
7c18968
72ec505
8da36d1
082b6c7
61ac56e
7250434
fa1f726
c41e451
e61ce45
bdf2d21
a65242b
3973ede
cdf706a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,10 @@ | |
|
|
||
| import { Event } from '../../../base/common/event.js'; | ||
| import { createDecorator } from '../../instantiation/common/instantiation.js'; | ||
| import { RawContextKey } from '../../contextkey/common/contextkey.js'; | ||
|
|
||
| /** The effective Marketplace auth provider, for `when`-clause driven welcome content. */ | ||
| export const CONTEXT_MARKETPLACE_AUTH_PROVIDER = new RawContextKey<string>('marketplaceAuthProvider', ''); | ||
|
|
||
| export const enum ExtensionGalleryResourceType { | ||
| ExtensionQueryService = 'ExtensionQueryService', | ||
|
|
@@ -68,7 +72,11 @@ export const enum ExtensionGalleryManifestStatus { | |
| Available = 'available', | ||
| RequiresSignIn = 'requiresSignIn', | ||
| AccessDenied = 'accessDenied', | ||
| Unavailable = 'unavailable' | ||
| Unavailable = 'unavailable', | ||
| /** A configured marketplace could not be reached — transient, unlike {@link Unavailable}. */ | ||
| Unreachable = 'unreachable', | ||
| /** The deployment cannot work as configured — e.g. a non-HTTPS service index under Entra auth. */ | ||
| Misconfigured = 'misconfigured' | ||
| } | ||
|
|
||
| export const IExtensionGalleryManifestService = createDecorator<IExtensionGalleryManifestService>('IExtensionGalleryManifestService'); | ||
|
|
@@ -98,3 +106,14 @@ export function getExtensionGalleryManifestResourceUri(manifest: IExtensionGalle | |
| } | ||
|
|
||
| export const ExtensionGalleryServiceUrlConfigKey = 'extensions.gallery.serviceUrl'; | ||
|
|
||
| export const ExtensionGalleryAuthProviderConfigKey = 'extensions.gallery.authProvider'; | ||
|
|
||
| /** Standard OpenID Connect scopes — enough to identify the user for the eligibility check. */ | ||
| export const PRIVATE_MARKETPLACE_SCOPES: string[] = ['openid', 'profile', 'email', 'offline_access']; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move this scopes to product.json just like scopes for github
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in a65242b — Followed These are plain OIDC scopes — we take the auth provider's default client id and One asymmetry worth naming: 🤖 This reply was drafted by an AI agent on behalf of Michael Cummings (MSFT) (@mcumming). |
||
|
|
||
| /** | ||
| * Interactive Microsoft sign-in, registered in the Electron layer and invoked by id from the | ||
| * browser-layer action so it need not cross the layer boundary. | ||
| */ | ||
| export const ExtensionGalleryMicrosoftSignInCommandId = 'workbench.extensions.marketplace.signInWithMicrosoft'; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. THere should be just one command for sign in and account service should handle signing in to microsoft or github
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 3973ede. await accessor.get(IExtensionGalleryAccountService).signIn();GitHub's 🤖 This reply was drafted by an AI agent on behalf of Michael Cummings (MSFT) (@mcumming). |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,9 +67,8 @@ import { createActionViewItem } from '../../../../platform/actions/browser/menuE | |
| import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; | ||
| import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; | ||
| import { KeyCode } from '../../../../base/common/keyCodes.js'; | ||
| import { IExtensionGalleryManifest, IExtensionGalleryManifestService, ExtensionGalleryManifestStatus } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; | ||
| import { IExtensionGalleryManifest, IExtensionGalleryManifestService, ExtensionGalleryManifestStatus, CONTEXT_MARKETPLACE_AUTH_PROVIDER } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; | ||
| import { URI } from '../../../../base/common/uri.js'; | ||
| import { DEFAULT_ACCOUNT_SIGN_IN_COMMAND } from '../../../services/accounts/browser/defaultAccount.js'; | ||
|
|
||
| export const ExtensionsSortByContext = new RawContextKey<string>('extensionsSortByValue', ''); | ||
| export const SearchMarketplaceExtensionsContext = new RawContextKey<boolean>('searchMarketplaceExtensions', false); | ||
|
|
@@ -146,7 +145,12 @@ export class ExtensionsViewletViewsContribution extends Disposable implements IW | |
| ContextKeyExpr.or( | ||
| ContextKeyExpr.has('searchMarketplaceExtensions'), ContextKeyExpr.and(DefaultViewsContext) | ||
| ), | ||
| ContextKeyExpr.or(CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn), CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.AccessDenied)) | ||
| ContextKeyExpr.or( | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn), | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.AccessDenied), | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.Misconfigured), | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.Unreachable) | ||
| ) | ||
| ), | ||
| order: -1, | ||
| }); | ||
|
|
@@ -155,14 +159,39 @@ export class ExtensionsViewletViewsContribution extends Disposable implements IW | |
| viewRegistry.registerViews(viewDescriptors, this.container); | ||
|
|
||
| viewRegistry.registerViewWelcomeContent('workbench.views.extensions.marketplaceAccess', { | ||
| content: localize('sign in', "[Sign in to access Extensions Marketplace]({0})", `command:${DEFAULT_ACCOUNT_SIGN_IN_COMMAND}`), | ||
| when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn) | ||
| content: localize('sign in microsoft', "[Sign in with your Microsoft account]({0}) to access the Extensions Marketplace.", `command:workbench.extensions.actions.gallery.signIn`), | ||
| when: ContextKeyExpr.and( | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn), | ||
| CONTEXT_MARKETPLACE_AUTH_PROVIDER.isEqualTo('microsoft') | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid hardcoding provider ids. They should be read only at one place which should be gallery accout service. The sign in button should just say Sign In - why does user has to know if it is github or microsoft if they have to sign in anyway - the sign in page anyway shows right
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 3973ede — back to a single status-gated welcome entry, same shape as You were right that the fork bought nothing: both blocks already invoked the same command and differed only in the label. Collapsing them left 🤖 This reply was drafted by an AI agent on behalf of Michael Cummings (MSFT) (@mcumming). |
||
| ) | ||
| }); | ||
|
|
||
| viewRegistry.registerViewWelcomeContent('workbench.views.extensions.marketplaceAccess', { | ||
| content: localize('sign in github', "[Sign in with GitHub]({0}) to access the Extensions Marketplace.", `command:workbench.extensions.actions.gallery.signIn`), | ||
| when: ContextKeyExpr.and( | ||
| CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.RequiresSignIn), | ||
| ContextKeyExpr.or( | ||
| CONTEXT_MARKETPLACE_AUTH_PROVIDER.isEqualTo('github'), | ||
| ContextKeyExpr.not('marketplaceAuthProvider') | ||
| ) | ||
| ) | ||
| }); | ||
|
|
||
| // Access denied applies to every provider (microsoft/github/default), so gate on status alone. | ||
| viewRegistry.registerViewWelcomeContent('workbench.views.extensions.marketplaceAccess', { | ||
| content: localize('access denied', "Your account does not have access to the Extensions Marketplace. Please contact your administrator."), | ||
| when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.AccessDenied) | ||
| }); | ||
|
|
||
| viewRegistry.registerViewWelcomeContent('workbench.views.extensions.marketplaceAccess', { | ||
| content: localize('marketplace misconfigured', "The Extensions Marketplace is not configured correctly and cannot be reached. Please contact your administrator."), | ||
| when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.Misconfigured) | ||
| }); | ||
|
|
||
| viewRegistry.registerViewWelcomeContent('workbench.views.extensions.marketplaceAccess', { | ||
| content: localize('marketplace unreachable', "The Extensions Marketplace is currently unavailable. Check your network connection and [try again]({0}).", `command:workbench.action.reloadWindow`), | ||
| when: CONTEXT_EXTENSIONS_GALLERY_STATUS.isEqualTo(ExtensionGalleryManifestStatus.Unreachable) | ||
| }); | ||
| } | ||
|
|
||
| private createDefaultExtensionsViewDescriptors(): IViewDescriptor[] { | ||
|
|
@@ -1161,6 +1190,12 @@ export class ExtensionMarketplaceStatusUpdater extends Disposable implements IWo | |
| case ExtensionGalleryManifestStatus.AccessDenied: | ||
| badge = new WarningBadge(() => localize('accessDenied', "Access denied to marketplace")); | ||
| break; | ||
| case ExtensionGalleryManifestStatus.Misconfigured: | ||
| badge = new WarningBadge(() => localize('marketplaceMisconfigured', "Marketplace is misconfigured")); | ||
| break; | ||
| case ExtensionGalleryManifestStatus.Unreachable: | ||
| badge = new WarningBadge(() => localize('marketplaceUnreachable', "Marketplace is currently unavailable")); | ||
| break; | ||
| } | ||
|
|
||
| if (badge) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| * Licensed under the MIT License. See License.txt in the project root for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| /** The authentication provider that gates Private Marketplace access. */ | ||
| export type ExtensionGalleryAccessProviderId = 'github' | 'microsoft'; | ||
|
|
||
| /** The service index rejected the request on authentication grounds (401/403). */ | ||
| export class MarketplaceAuthRequiredError extends Error { | ||
| constructor(readonly statusCode: number) { | ||
| super(`Extension gallery request requires authentication (status ${statusCode}).`); | ||
| } | ||
| } | ||
|
|
||
| /** The marketplace refused this client outright — any 4xx other than 401/403. */ | ||
| export class MarketplaceClientRejectedError extends Error { | ||
| constructor(readonly statusCode: number, message: string) { | ||
| super(message); | ||
| } | ||
| } | ||
|
|
||
| /** A configured `microsoft` provider is downgraded to `github` until Entra auth ships. */ | ||
| export function getEffectiveAuthProvider(configuredProvider: string | undefined, entraAuthEnabled: boolean): ExtensionGalleryAccessProviderId { | ||
| return configuredProvider === 'microsoft' && entraAuthEnabled ? 'microsoft' : 'github'; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets not introduce more status code unless necessay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already done —
UnreachableandMisconfiguredcame out in e61ce45, so the enum is back to the four values onmain. The UI sites that switched on them went with it.🤖 This reply was drafted by an AI agent on behalf of Michael Cummings (MSFT) (@mcumming).