@microsoft/vscode-azext-azureauth 4.2.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/{out → dist/cjs}/src/AzureAuthentication.d.ts +2 -2
  2. package/dist/cjs/src/AzureDevOpsSubscriptionProvider.js +216 -0
  3. package/dist/cjs/src/NotSignedInError.js +63 -0
  4. package/dist/cjs/src/VSCodeAzureSubscriptionProvider.js +342 -0
  5. package/{out → dist/cjs}/src/getSessionFromVSCode.d.ts +3 -3
  6. package/dist/cjs/src/getSessionFromVSCode.js +108 -0
  7. package/dist/cjs/src/index.d.ts +56 -0
  8. package/{out → dist/cjs}/src/index.js +1 -1
  9. package/dist/cjs/src/signInToTenant.js +79 -0
  10. package/dist/cjs/src/utils/configuredAzureEnv.js +128 -0
  11. package/dist/cjs/src/utils/getUnauthenticatedTenants.js +21 -0
  12. package/dist/esm/src/AzureAuthentication.d.ts +21 -0
  13. package/dist/esm/src/AzureAuthentication.js +6 -0
  14. package/dist/esm/src/AzureDevOpsSubscriptionProvider.d.ts +68 -0
  15. package/dist/esm/src/AzureDevOpsSubscriptionProvider.js +211 -0
  16. package/dist/esm/src/AzureSubscription.d.ts +49 -0
  17. package/dist/esm/src/AzureSubscription.js +6 -0
  18. package/dist/esm/src/AzureSubscriptionProvider.d.ts +82 -0
  19. package/dist/esm/src/AzureSubscriptionProvider.js +6 -0
  20. package/dist/esm/src/AzureTenant.d.ts +5 -0
  21. package/dist/esm/src/AzureTenant.js +6 -0
  22. package/dist/esm/src/NotSignedInError.d.ts +15 -0
  23. package/{out → dist/esm}/src/NotSignedInError.js +4 -9
  24. package/dist/esm/src/VSCodeAzureSubscriptionProvider.d.ts +117 -0
  25. package/dist/esm/src/VSCodeAzureSubscriptionProvider.js +305 -0
  26. package/dist/esm/src/getSessionFromVSCode.d.ts +13 -0
  27. package/dist/esm/src/getSessionFromVSCode.js +72 -0
  28. package/dist/esm/src/index.d.ts +56 -0
  29. package/{out/src/index.d.ts → dist/esm/src/index.js} +6 -1
  30. package/dist/esm/src/signInToTenant.d.ts +6 -0
  31. package/dist/esm/src/signInToTenant.js +43 -0
  32. package/dist/esm/src/utils/configuredAzureEnv.d.ts +24 -0
  33. package/dist/esm/src/utils/configuredAzureEnv.js +90 -0
  34. package/dist/esm/src/utils/getUnauthenticatedTenants.d.ts +6 -0
  35. package/dist/esm/src/utils/getUnauthenticatedTenants.js +18 -0
  36. package/dist/esm/src/utils/isAuthenticationWwwAuthenticateRequest.d.ts +2 -0
  37. package/dist/esm/src/utils/isAuthenticationWwwAuthenticateRequest.js +12 -0
  38. package/package.json +13 -12
  39. package/out/src/AzureDevOpsSubscriptionProvider.js +0 -252
  40. package/out/src/VSCodeAzureSubscriptionProvider.js +0 -384
  41. package/out/src/getSessionFromVSCode.js +0 -76
  42. package/out/src/signInToTenant.js +0 -64
  43. package/out/src/utils/configuredAzureEnv.js +0 -94
  44. package/out/src/utils/getUnauthenticatedTenants.js +0 -52
  45. /package/{out → dist/cjs}/src/AzureAuthentication.js +0 -0
  46. /package/{out → dist/cjs}/src/AzureDevOpsSubscriptionProvider.d.ts +0 -0
  47. /package/{out → dist/cjs}/src/AzureSubscription.d.ts +0 -0
  48. /package/{out → dist/cjs}/src/AzureSubscription.js +0 -0
  49. /package/{out → dist/cjs}/src/AzureSubscriptionProvider.d.ts +0 -0
  50. /package/{out → dist/cjs}/src/AzureSubscriptionProvider.js +0 -0
  51. /package/{out → dist/cjs}/src/AzureTenant.d.ts +0 -0
  52. /package/{out → dist/cjs}/src/AzureTenant.js +0 -0
  53. /package/{out → dist/cjs}/src/NotSignedInError.d.ts +0 -0
  54. /package/{out → dist/cjs}/src/VSCodeAzureSubscriptionProvider.d.ts +0 -0
  55. /package/{out → dist/cjs}/src/signInToTenant.d.ts +0 -0
  56. /package/{out → dist/cjs}/src/utils/configuredAzureEnv.d.ts +0 -0
  57. /package/{out → dist/cjs}/src/utils/getUnauthenticatedTenants.d.ts +0 -0
  58. /package/{out → dist/cjs}/src/utils/isAuthenticationWwwAuthenticateRequest.d.ts +0 -0
  59. /package/{out → dist/cjs}/src/utils/isAuthenticationWwwAuthenticateRequest.js +0 -0
@@ -13,9 +13,9 @@ export interface AzureAuthentication {
13
13
  /**
14
14
  * Gets a VS Code authentication session for an Azure subscription.
15
15
  *
16
- * @param scopes - The scopes for which the authentication is needed.
16
+ * @param scopeListOrRequest - The scopes or request for which the authentication is needed.
17
17
  *
18
18
  * @returns A VS Code authentication session or undefined, if none could be obtained.
19
19
  */
20
- getSessionWithScopes(scopes: string[] | vscode.AuthenticationWwwAuthenticateRequest): vscode.ProviderResult<vscode.AuthenticationSession>;
20
+ getSessionWithScopes(scopeListOrRequest: string[] | vscode.AuthenticationWwwAuthenticateRequest): vscode.ProviderResult<vscode.AuthenticationSession>;
21
21
  }
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.AzureDevOpsSubscriptionProvider = void 0;
8
+ exports.createAzureDevOpsSubscriptionProviderFactory = createAzureDevOpsSubscriptionProviderFactory;
9
+ const vscode_1 = require("vscode");
10
+ const configuredAzureEnv_1 = require("./utils/configuredAzureEnv");
11
+ let azureDevOpsSubscriptionProvider;
12
+ function createAzureDevOpsSubscriptionProviderFactory(initializer) {
13
+ return async () => {
14
+ azureDevOpsSubscriptionProvider ??= new AzureDevOpsSubscriptionProvider(initializer);
15
+ return azureDevOpsSubscriptionProvider;
16
+ };
17
+ }
18
+ /**
19
+ * AzureSubscriptionProvider implemented to authenticate via federated DevOps service connection, using workflow identity federation
20
+ * To learn how to configure your DevOps environment to use this provider, refer to the README.md
21
+ * NOTE: This provider is only available when running in an Azure DevOps pipeline
22
+ * Reference: https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation
23
+ */
24
+ class AzureDevOpsSubscriptionProvider {
25
+ _tokenCredential;
26
+ /**
27
+ * The resource ID of the Azure DevOps federated service connection,
28
+ * which can be found on the `resourceId` field of the URL at the address bar
29
+ * when viewing the service connection in the Azure DevOps portal
30
+ */
31
+ _SERVICE_CONNECTION_ID;
32
+ /**
33
+ * The `Tenant ID` field of the service connection properties
34
+ */
35
+ _DOMAIN;
36
+ /**
37
+ * The `Service Principal Id` field of the service connection properties
38
+ */
39
+ _CLIENT_ID;
40
+ constructor({ serviceConnectionId, domain, clientId }) {
41
+ if (!serviceConnectionId || !domain || !clientId) {
42
+ throw new Error(`Missing initializer values to identify Azure DevOps federated service connection\n
43
+ Values provided:\n
44
+ serviceConnectionId: ${serviceConnectionId ? "✅" : "❌"}\n
45
+ domain: ${domain ? "✅" : "❌"}\n
46
+ clientId: ${clientId ? "✅" : "❌"}\n
47
+ `);
48
+ }
49
+ this._SERVICE_CONNECTION_ID = serviceConnectionId;
50
+ this._DOMAIN = domain;
51
+ this._CLIENT_ID = clientId;
52
+ }
53
+ async getSubscriptions(_filter) {
54
+ // ignore the filter setting because not every consumer of this provider will use the Resources extension
55
+ const results = [];
56
+ for (const tenant of await this.getTenants()) {
57
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
58
+ const tenantId = tenant.tenantId;
59
+ results.push(...await this.getSubscriptionsForTenant(tenantId));
60
+ }
61
+ const sortSubscriptions = (subscriptions) => subscriptions.sort((a, b) => a.name.localeCompare(b.name));
62
+ return sortSubscriptions(results);
63
+ }
64
+ async isSignedIn() {
65
+ return !!this._tokenCredential;
66
+ }
67
+ async signIn() {
68
+ this._tokenCredential = await getTokenCredential(this._SERVICE_CONNECTION_ID, this._DOMAIN, this._CLIENT_ID);
69
+ return !!this._tokenCredential;
70
+ }
71
+ async signOut() {
72
+ this._tokenCredential = undefined;
73
+ }
74
+ async getTenants() {
75
+ return [{
76
+ tenantId: this._tokenCredential?.tenantId,
77
+ account: {
78
+ id: "test-account-id",
79
+ label: "test-account",
80
+ }
81
+ }];
82
+ }
83
+ /**
84
+ * Gets the subscriptions for a given tenant.
85
+ *
86
+ * @param tenantId The tenant ID to get subscriptions for.
87
+ *
88
+ * @returns The list of subscriptions for the tenant.
89
+ */
90
+ async getSubscriptionsForTenant(tenantId) {
91
+ const { client, credential, authentication } = await this.getSubscriptionClient(tenantId);
92
+ const environment = (0, configuredAzureEnv_1.getConfiguredAzureEnv)();
93
+ const subscriptions = [];
94
+ for await (const subscription of client.subscriptions.list()) {
95
+ subscriptions.push({
96
+ authentication,
97
+ environment: environment,
98
+ credential: credential,
99
+ isCustomCloud: environment.isCustomCloud,
100
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
101
+ name: subscription.displayName,
102
+ subscriptionId: subscription.subscriptionId,
103
+ /* eslint-enable @typescript-eslint/no-non-null-assertion */
104
+ tenantId,
105
+ account: {
106
+ id: "test-account-id",
107
+ label: "test-account",
108
+ },
109
+ });
110
+ }
111
+ return subscriptions;
112
+ }
113
+ /**
114
+ * Gets a fully-configured subscription client for a given tenant ID
115
+ *
116
+ * @param tenantId (Optional) The tenant ID to get a client for
117
+ *
118
+ * @returns A client, the credential used by the client, and the authentication function
119
+ */
120
+ async getSubscriptionClient(_tenantId, scopes) {
121
+ const armSubs = await import('@azure/arm-resources-subscriptions');
122
+ if (!this._tokenCredential) {
123
+ throw new Error('Not signed in');
124
+ }
125
+ const accessToken = (await this._tokenCredential?.getToken("https://management.azure.com/.default"))?.token || '';
126
+ const getSession = () => {
127
+ return {
128
+ accessToken,
129
+ id: this._tokenCredential?.tenantId || '',
130
+ account: {
131
+ id: this._tokenCredential?.tenantId || '',
132
+ label: this._tokenCredential?.tenantId || '',
133
+ },
134
+ tenantId: this._tokenCredential?.tenantId || '',
135
+ scopes: scopes || [],
136
+ };
137
+ };
138
+ return {
139
+ client: new armSubs.SubscriptionClient(this._tokenCredential),
140
+ credential: this._tokenCredential,
141
+ authentication: {
142
+ getSession,
143
+ getSessionWithScopes: getSession,
144
+ }
145
+ };
146
+ }
147
+ onDidSignIn = () => { return new vscode_1.Disposable(() => { }); };
148
+ onDidSignOut = () => { return new vscode_1.Disposable(() => { }); };
149
+ }
150
+ exports.AzureDevOpsSubscriptionProvider = AzureDevOpsSubscriptionProvider;
151
+ /*
152
+ * @param serviceConnectionId The resource ID of the Azure DevOps federated service connection,
153
+ * which can be found on the `resourceId` field of the URL at the address bar when viewing the service connection in the Azure DevOps portal
154
+ * @param domain The `Tenant ID` field of the service connection properties
155
+ * @param clientId The `Service Principal Id` field of the service connection properties
156
+ */
157
+ async function getTokenCredential(serviceConnectionId, domain, clientId) {
158
+ if (!process.env.AGENT_BUILDDIRECTORY) {
159
+ // Assume that AGENT_BUILDDIRECTORY is set if running in an Azure DevOps pipeline.
160
+ // So when not running in an Azure DevOps pipeline, throw an error since we cannot use the DevOps federated service connection credential.
161
+ throw new Error(`Cannot create DevOps federated service connection credential outside of an Azure DevOps pipeline.`);
162
+ }
163
+ else {
164
+ console.log(`Creating DevOps federated service connection credential for service connection..`);
165
+ // Pre-defined DevOps variable reference: https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops
166
+ const systemAccessToken = process.env.SYSTEM_ACCESSTOKEN;
167
+ const teamFoundationCollectionUri = process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI;
168
+ const teamProjectId = process.env.SYSTEM_TEAMPROJECTID;
169
+ const planId = process.env.SYSTEM_PLANID;
170
+ const jobId = process.env.SYSTEM_JOBID;
171
+ if (!systemAccessToken || !teamFoundationCollectionUri || !teamProjectId || !planId || !jobId) {
172
+ throw new Error(`Azure DevOps environment variables are not set.\n
173
+ process.env.SYSTEM_ACCESSTOKEN: ${process.env.SYSTEM_ACCESSTOKEN ? "✅" : "❌"}\n
174
+ process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: ${process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI ? "✅" : "❌"}\n
175
+ process.env.SYSTEM_TEAMPROJECTID: ${process.env.SYSTEM_TEAMPROJECTID ? "✅" : "❌"}\n
176
+ process.env.SYSTEM_PLANID: ${process.env.SYSTEM_PLANID ? "✅" : "❌"}\n
177
+ process.env.SYSTEM_JOBID: ${process.env.SYSTEM_JOBID ? "✅" : "❌"}\n
178
+ REMEMBER: process.env.SYSTEM_ACCESSTOKEN must be explicitly mapped!\n
179
+ https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#systemaccesstoken
180
+ `);
181
+ }
182
+ const oidcRequestUrl = `${teamFoundationCollectionUri}${teamProjectId}/_apis/distributedtask/hubs/build/plans/${planId}/jobs/${jobId}/oidctoken?api-version=7.1-preview.1&serviceConnectionId=${serviceConnectionId}`;
183
+ const { ClientAssertionCredential } = await import("@azure/identity");
184
+ return new ClientAssertionCredential(domain, clientId, async () => await requestOidcToken(oidcRequestUrl, systemAccessToken));
185
+ }
186
+ }
187
+ /**
188
+ * API reference: https://learn.microsoft.com/en-us/rest/api/azure/devops/distributedtask/oidctoken/create
189
+ */
190
+ async function requestOidcToken(oidcRequestUrl, systemAccessToken) {
191
+ const { ServiceClient } = await import('@azure/core-client');
192
+ const { createHttpHeaders, createPipelineRequest } = await import('@azure/core-rest-pipeline');
193
+ const genericClient = new ServiceClient();
194
+ const request = createPipelineRequest({
195
+ url: oidcRequestUrl,
196
+ method: "POST",
197
+ headers: createHttpHeaders({
198
+ "Content-Type": "application/json",
199
+ "Authorization": `Bearer ${systemAccessToken}`
200
+ })
201
+ });
202
+ const response = await genericClient.sendRequest(request);
203
+ const body = response.bodyAsText?.toString() || "";
204
+ if (response.status !== 200) {
205
+ throw new Error(`Failed to get OIDC token:\n
206
+ Response status: ${response.status}\n
207
+ Response body: ${body}\n
208
+ Response headers: ${JSON.stringify(response.headers.toJSON())}
209
+ `);
210
+ }
211
+ else {
212
+ console.log(`Successfully got OIDC token with status ${response.status}`);
213
+ }
214
+ return JSON.parse(body).oidcToken;
215
+ }
216
+ //# sourceMappingURL=AzureDevOpsSubscriptionProvider.js.map
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.NotSignedInError = void 0;
41
+ exports.isNotSignedInError = isNotSignedInError;
42
+ const vscode = __importStar(require("vscode"));
43
+ /**
44
+ * An error indicating the user is not signed in.
45
+ */
46
+ class NotSignedInError extends Error {
47
+ isNotSignedInError = true;
48
+ constructor() {
49
+ super(vscode.l10n.t('You are not signed in to an Azure account. Please sign in.'));
50
+ }
51
+ }
52
+ exports.NotSignedInError = NotSignedInError;
53
+ /**
54
+ * Tests if an object is a `NotSignedInError`. This should be used instead of `instanceof`.
55
+ *
56
+ * @param error The object to test
57
+ *
58
+ * @returns True if the object is a NotSignedInError, false otherwise
59
+ */
60
+ function isNotSignedInError(error) {
61
+ return !!error && typeof error === 'object' && error.isNotSignedInError === true;
62
+ }
63
+ //# sourceMappingURL=NotSignedInError.js.map
@@ -0,0 +1,342 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License. See License.txt in the project root for license information.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.VSCodeAzureSubscriptionProvider = void 0;
41
+ const vscode = __importStar(require("vscode"));
42
+ const getSessionFromVSCode_1 = require("./getSessionFromVSCode");
43
+ const NotSignedInError_1 = require("./NotSignedInError");
44
+ const configuredAzureEnv_1 = require("./utils/configuredAzureEnv");
45
+ const isAuthenticationWwwAuthenticateRequest_1 = require("./utils/isAuthenticationWwwAuthenticateRequest");
46
+ const EventDebounce = 5 * 1000; // 5 seconds
47
+ /**
48
+ * A class for obtaining Azure subscription information using VSCode's built-in authentication
49
+ * provider.
50
+ */
51
+ class VSCodeAzureSubscriptionProvider extends vscode.Disposable {
52
+ logger;
53
+ onDidSignInEmitter = new vscode.EventEmitter();
54
+ lastSignInEventFired = 0;
55
+ suppressSignInEvents = false;
56
+ onDidSignOutEmitter = new vscode.EventEmitter();
57
+ lastSignOutEventFired = 0;
58
+ // So that customers can easily share logs, try to only log PII using trace level
59
+ constructor(logger) {
60
+ const disposable = vscode.authentication.onDidChangeSessions(async (e) => {
61
+ // Ignore any sign in that isn't for the configured auth provider
62
+ if (e.provider.id !== (0, configuredAzureEnv_1.getConfiguredAuthProviderId)()) {
63
+ return;
64
+ }
65
+ if (await this.isSignedIn()) {
66
+ if (!this.suppressSignInEvents && Date.now() > this.lastSignInEventFired + EventDebounce) {
67
+ this.lastSignInEventFired = Date.now();
68
+ this.onDidSignInEmitter.fire();
69
+ }
70
+ }
71
+ else if (Date.now() > this.lastSignOutEventFired + EventDebounce) {
72
+ this.lastSignOutEventFired = Date.now();
73
+ this.onDidSignOutEmitter.fire();
74
+ }
75
+ });
76
+ super(() => {
77
+ this.onDidSignInEmitter.dispose();
78
+ this.onDidSignOutEmitter.dispose();
79
+ disposable.dispose();
80
+ });
81
+ this.logger = logger;
82
+ }
83
+ /**
84
+ * Gets a list of tenants available to the user.
85
+ * Use {@link isSignedIn} to check if the user is signed in to a particular tenant.
86
+ *
87
+ * @param account (Optional) A specific account to get tenants for. If not provided, all accounts will be used.
88
+ *
89
+ * @returns A list of tenants.
90
+ */
91
+ async getTenants(account) {
92
+ const startTimeMs = Date.now();
93
+ const results = [];
94
+ for await (account of account ? [account] : await vscode.authentication.getAccounts((0, configuredAzureEnv_1.getConfiguredAuthProviderId)())) {
95
+ // Added check. Without this the getSubscriptionClient function throws the NotSignedInError
96
+ if (await this.isSignedIn(undefined, account)) {
97
+ const { client } = await this.getSubscriptionClient(account, undefined, undefined);
98
+ for await (const tenant of client.tenants.list()) {
99
+ results.push({ ...tenant, account });
100
+ }
101
+ }
102
+ }
103
+ const endTimeMs = Date.now();
104
+ this.logger?.debug(`auth: Got ${results.length} tenants for account "${account?.label}" in ${endTimeMs - startTimeMs}ms`);
105
+ return results;
106
+ }
107
+ /**
108
+ * Gets a list of Azure subscriptions available to the user.
109
+ *
110
+ * @param filter - Whether to filter the list returned. When:
111
+ * - `true`: according to the list returned by `getTenantFilters()` and `getSubscriptionFilters()`.
112
+ * - `false`: return all subscriptions.
113
+ * - `GetSubscriptionsFilter`: according to the values in the filter.
114
+ *
115
+ * Optional, default true.
116
+ *
117
+ * @returns A list of Azure subscriptions. The list is sorted by subscription name.
118
+ * The list can contain duplicate subscriptions if they come from different accounts.
119
+ *
120
+ * @throws A {@link NotSignedInError} If the user is not signed in to Azure.
121
+ * Use {@link isSignedIn} and/or {@link signIn} before this method to ensure
122
+ * the user is signed in.
123
+ */
124
+ async getSubscriptions(filter = true) {
125
+ this.logger?.debug('auth: Loading subscriptions...');
126
+ const startTime = Date.now();
127
+ const configuredTenantFilter = await this.getTenantFilters();
128
+ const tenantIdsToFilterBy =
129
+ // Only filter by the tenant ID option if it is provided
130
+ (typeof filter === 'object' && filter.tenantId ? [filter.tenantId] :
131
+ // Only filter by the configured filter if `filter` is true AND there are tenants in the configured filter
132
+ filter === true && configuredTenantFilter.length > 0 ? configuredTenantFilter :
133
+ undefined);
134
+ const allSubscriptions = [];
135
+ let accountCount; // only used for logging
136
+ try {
137
+ this.suppressSignInEvents = true;
138
+ // Get the list of tenants from each account (filtered or all)
139
+ const accounts = typeof filter === 'object' && filter.account ? [filter.account] : await vscode.authentication.getAccounts((0, configuredAzureEnv_1.getConfiguredAuthProviderId)());
140
+ accountCount = accounts.length;
141
+ for (const account of accounts) {
142
+ for (const tenant of await this.getTenants(account)) {
143
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
144
+ const tenantId = tenant.tenantId;
145
+ if (tenantIdsToFilterBy?.includes(tenantId) === false) {
146
+ continue;
147
+ }
148
+ // For each tenant, get the list of subscriptions
149
+ allSubscriptions.push(...await this.getSubscriptionsForTenant(account, tenantId));
150
+ }
151
+ // list subscriptions for the home tenant
152
+ allSubscriptions.push(...await this.getSubscriptionsForTenant(account));
153
+ }
154
+ }
155
+ finally {
156
+ this.suppressSignInEvents = false;
157
+ }
158
+ // It's possible that by listing subscriptions in all tenants and the "home" tenant there could be duplicate subscriptions
159
+ // Thus, we remove duplicate subscriptions. However, if multiple accounts have the same subscription, we keep them.
160
+ const subscriptionMap = new Map();
161
+ allSubscriptions.forEach(sub => subscriptionMap.set(`${sub.account.id}/${sub.subscriptionId}`, sub));
162
+ const uniqueSubscriptions = Array.from(subscriptionMap.values());
163
+ const endTime = Date.now();
164
+ this.logger?.debug(`auth: Got ${uniqueSubscriptions.length} subscriptions from ${accountCount} accounts in ${endTime - startTime}ms`);
165
+ const sortSubscriptions = (subscriptions) => subscriptions.sort((a, b) => a.name.localeCompare(b.name));
166
+ const subscriptionIds = await this.getSubscriptionFilters();
167
+ if (filter === true && !!subscriptionIds.length) { // If the list is empty it is treated as "no filter"
168
+ return sortSubscriptions(uniqueSubscriptions.filter(sub => subscriptionIds.includes(sub.subscriptionId)));
169
+ }
170
+ return sortSubscriptions(uniqueSubscriptions);
171
+ }
172
+ /**
173
+ * Checks to see if a user is signed in.
174
+ *
175
+ * @param tenantId (Optional) Provide to check if a user is signed in to a specific tenant.
176
+ * @param account (Optional) Provide to check if a user is signed in to a specific account.
177
+ *
178
+ * @returns True if the user is signed in, false otherwise.
179
+ *
180
+ * If no tenant or account is provided, then
181
+ * checks all accounts for a session.
182
+ */
183
+ async isSignedIn(tenantId, account) {
184
+ async function silentlyCheckForSession(tenantId, account) {
185
+ return !!await (0, getSessionFromVSCode_1.getSessionFromVSCode)([], tenantId, { createIfNone: false, silent: true, account });
186
+ }
187
+ const innerIsSignedIn = async () => {
188
+ // If no tenant or account is provided, then check all accounts for a session
189
+ if (!account && !tenantId) {
190
+ const accounts = await vscode.authentication.getAccounts((0, configuredAzureEnv_1.getConfiguredAuthProviderId)());
191
+ if (accounts.length === 0) {
192
+ return false;
193
+ }
194
+ for (const account of accounts) {
195
+ if (await silentlyCheckForSession(tenantId, account)) {
196
+ // If any account has a session, then return true because the user is signed in
197
+ return true;
198
+ }
199
+ }
200
+ }
201
+ return silentlyCheckForSession(tenantId, account);
202
+ };
203
+ const result = await innerIsSignedIn();
204
+ this.logger?.trace(`auth: isSignedIn returned ${result} (account="${account?.label ?? 'none'}") (tenantId="${tenantId ?? 'none'}")`);
205
+ return result;
206
+ }
207
+ /**
208
+ * Asks the user to sign in or pick an account to use.
209
+ *
210
+ * @param tenantId (Optional) Provide to sign in to a specific tenant.
211
+ * @param account (Optional) Provide to sign in to a specific account.
212
+ *
213
+ * @returns True if the user is signed in, false otherwise.
214
+ */
215
+ async signIn(tenantId, account) {
216
+ this.logger?.debug(`auth: Signing in (account="${account?.label ?? 'none'}") (tenantId="${tenantId ?? 'none'}")`);
217
+ const session = await (0, getSessionFromVSCode_1.getSessionFromVSCode)([], tenantId, {
218
+ createIfNone: true,
219
+ // If no account is provided, then clear the session preference which tells VS Code to show the account picker
220
+ clearSessionPreference: !account,
221
+ account,
222
+ });
223
+ return !!session;
224
+ }
225
+ /**
226
+ * An event that is fired when the user signs in. Debounced to fire at most once every 5 seconds.
227
+ */
228
+ onDidSignIn = this.onDidSignInEmitter.event;
229
+ /**
230
+ * Signs the user out
231
+ *
232
+ * @deprecated Not currently supported by VS Code auth providers
233
+ */
234
+ signOut() {
235
+ throw new Error(vscode.l10n.t('Signing out programmatically is not supported. You must sign out by selecting the account in the Accounts menu and choosing Sign Out.'));
236
+ }
237
+ /**
238
+ * An event that is fired when the user signs out. Debounced to fire at most once every 5 seconds.
239
+ */
240
+ onDidSignOut = this.onDidSignOutEmitter.event;
241
+ /**
242
+ * Gets the tenant filters that are configured in `azureResourceGroups.selectedSubscriptions`. To
243
+ * override the settings with a custom filter, implement a child class with `getSubscriptionFilters()`
244
+ * and/or `getTenantFilters()` overridden.
245
+ *
246
+ * If no values are returned by `getTenantFilters()`, then all tenants will be scanned for subscriptions.
247
+ *
248
+ * @returns A list of tenant IDs that are configured in `azureResourceGroups.selectedSubscriptions`.
249
+ */
250
+ async getTenantFilters() {
251
+ const config = vscode.workspace.getConfiguration('azureResourceGroups');
252
+ const fullSubscriptionIds = config.get('selectedSubscriptions', []);
253
+ return fullSubscriptionIds.map(id => id.split('/')[0]);
254
+ }
255
+ /**
256
+ * Gets the subscription filters that are configured in `azureResourceGroups.selectedSubscriptions`. To
257
+ * override the settings with a custom filter, implement a child class with `getSubscriptionFilters()`
258
+ * and/or `getTenantFilters()` overridden.
259
+ *
260
+ * If no values are returned by `getSubscriptionFilters()`, then all subscriptions will be returned.
261
+ *
262
+ * @returns A list of subscription IDs that are configured in `azureResourceGroups.selectedSubscriptions`.
263
+ */
264
+ async getSubscriptionFilters() {
265
+ const config = vscode.workspace.getConfiguration('azureResourceGroups');
266
+ const fullSubscriptionIds = config.get('selectedSubscriptions', []);
267
+ return fullSubscriptionIds.map(id => id.split('/')[1]);
268
+ }
269
+ /**
270
+ * Gets the subscriptions for a given tenant.
271
+ *
272
+ * @param tenantId The tenant ID to get subscriptions for.
273
+ * @param account The account to get the subscriptions for.
274
+ *
275
+ * @returns The list of subscriptions for the tenant.
276
+ */
277
+ async getSubscriptionsForTenant(account, tenantId) {
278
+ // If the user is not signed in to this tenant or account, then return an empty list
279
+ // This is to prevent the NotSignedInError from being thrown in getSubscriptionClient
280
+ if (!await this.isSignedIn(tenantId, account)) {
281
+ return [];
282
+ }
283
+ const { client, credential, authentication } = await this.getSubscriptionClient(account, tenantId, undefined);
284
+ const environment = (0, configuredAzureEnv_1.getConfiguredAzureEnv)();
285
+ const subscriptions = [];
286
+ for await (const subscription of client.subscriptions.list()) {
287
+ subscriptions.push({
288
+ authentication: authentication,
289
+ environment: environment,
290
+ credential: credential,
291
+ isCustomCloud: environment.isCustomCloud,
292
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
293
+ name: subscription.displayName,
294
+ subscriptionId: subscription.subscriptionId,
295
+ tenantId: tenantId ?? subscription.tenantId,
296
+ /* eslint-enable @typescript-eslint/no-non-null-assertion */
297
+ account: account
298
+ });
299
+ }
300
+ return subscriptions;
301
+ }
302
+ /**
303
+ * Gets a fully-configured subscription client for a given tenant ID
304
+ *
305
+ * @param tenantId (Optional) The tenant ID to get a client for
306
+ * @param account The account that you would like to get the session for
307
+ *
308
+ * @returns A client, the credential used by the client, and the authentication function
309
+ */
310
+ async getSubscriptionClient(account, tenantId, scopes) {
311
+ const armSubs = await import('@azure/arm-resources-subscriptions');
312
+ const session = await (0, getSessionFromVSCode_1.getSessionFromVSCode)(scopes, tenantId, { createIfNone: false, silent: true, account });
313
+ if (!session) {
314
+ throw new NotSignedInError_1.NotSignedInError();
315
+ }
316
+ const credential = {
317
+ getToken: async () => {
318
+ return {
319
+ token: session.accessToken,
320
+ expiresOnTimestamp: 0
321
+ };
322
+ }
323
+ };
324
+ const configuredAzureEnv = (0, configuredAzureEnv_1.getConfiguredAzureEnv)();
325
+ const endpoint = configuredAzureEnv.resourceManagerEndpointUrl;
326
+ return {
327
+ client: new armSubs.SubscriptionClient(credential, { endpoint }),
328
+ credential: credential,
329
+ authentication: {
330
+ getSession: () => session,
331
+ getSessionWithScopes: (scopeListOrRequest) => {
332
+ // in order to handle a challenge, we must enable createIfNone so
333
+ // that we can prompt the user to step-up their session with MFA
334
+ // otherwise, never prompt the user
335
+ return (0, getSessionFromVSCode_1.getSessionFromVSCode)(scopeListOrRequest, tenantId, { ...((0, isAuthenticationWwwAuthenticateRequest_1.isAuthenticationWwwAuthenticateRequest)(scopeListOrRequest) ? { createIfNone: true } : { silent: true }), account });
336
+ },
337
+ }
338
+ };
339
+ }
340
+ }
341
+ exports.VSCodeAzureSubscriptionProvider = VSCodeAzureSubscriptionProvider;
342
+ //# sourceMappingURL=VSCodeAzureSubscriptionProvider.js.map
@@ -4,10 +4,10 @@ import * as vscode from "vscode";
4
4
  * * Passing the configured auth provider id
5
5
  * * Getting the list of scopes, adding the tenant id to the scope list if needed
6
6
  *
7
- * @param scopes - top-level resource scopes (e.g. http://management.azure.com, http://storage.azure.com) or .default scopes. All resources/scopes will be normalized to the `.default` scope for each resource.
8
- * Use `vscode.AuthenticationWwwAuthenticateRequest` if you need to pass in a challenge (WWW-Authenticate header). Note: Use of `vscode.AuthenticationWwwAuthenticateRequest` requires VS Code 1.104 or newer.
7
+ * @param scopeOrListOrRequest - top-level resource scopes (e.g. http://management.azure.com, http://storage.azure.com) or .default scopes. All resources/scopes will be normalized to the `.default` scope for each resource.
8
+ * Use `vscode.AuthenticationWwwAuthenticateRequest` if you need to pass in a challenge (WWW-Authenticate header). Note: Use of `vscode.AuthenticationWwwAuthenticateRequest` requires VS Code 1.105.0 or newer.
9
9
  * @param tenantId - (Optional) The tenant ID, will be added to the scopes
10
10
  * @param options - see {@link vscode.AuthenticationGetSessionOptions}
11
11
  * @returns An authentication session if available, or undefined if there are no sessions
12
12
  */
13
- export declare function getSessionFromVSCode(scopes?: string | string[] | vscode.AuthenticationWwwAuthenticateRequest, tenantId?: string, options?: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession | undefined>;
13
+ export declare function getSessionFromVSCode(scopeOrListOrRequest?: string | string[] | vscode.AuthenticationWwwAuthenticateRequest, tenantId?: string, options?: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession | undefined>;