@azure/identity 3.0.0-alpha.20220914.2 → 3.0.0-alpha.20220919.5

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.

Potentially problematic release.


This version of @azure/identity might be problematic. Click here for more details.

Files changed (130) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1901 -1857
  3. package/dist/index.js.map +1 -1
  4. package/dist-esm/src/client/identityClient.js +5 -9
  5. package/dist-esm/src/client/identityClient.js.map +1 -1
  6. package/dist-esm/src/constants.js +4 -0
  7. package/dist-esm/src/constants.js.map +1 -1
  8. package/dist-esm/src/credentials/authorizationCodeCredential.browser.js.map +1 -1
  9. package/dist-esm/src/credentials/authorizationCodeCredential.js +10 -3
  10. package/dist-esm/src/credentials/authorizationCodeCredential.js.map +1 -1
  11. package/dist-esm/src/credentials/authorizationCodeCredentialOptions.js +4 -0
  12. package/dist-esm/src/credentials/authorizationCodeCredentialOptions.js.map +1 -0
  13. package/dist-esm/src/credentials/azureApplicationCredential.browser.js.map +1 -1
  14. package/dist-esm/src/credentials/azureApplicationCredential.js +1 -1
  15. package/dist-esm/src/credentials/azureApplicationCredential.js.map +1 -1
  16. package/dist-esm/src/credentials/azureApplicationCredentialOptions.js +4 -0
  17. package/dist-esm/src/credentials/azureApplicationCredentialOptions.js.map +1 -0
  18. package/dist-esm/src/credentials/azureCliCredential.browser.js.map +1 -1
  19. package/dist-esm/src/credentials/azureCliCredential.js +6 -9
  20. package/dist-esm/src/credentials/azureCliCredential.js.map +1 -1
  21. package/dist-esm/src/credentials/azureCliCredentialOptions.js.map +1 -1
  22. package/dist-esm/src/credentials/azurePowerShellCredential.browser.js.map +1 -1
  23. package/dist-esm/src/credentials/azurePowerShellCredential.js +5 -8
  24. package/dist-esm/src/credentials/azurePowerShellCredential.js.map +1 -1
  25. package/dist-esm/src/credentials/azurePowerShellCredentialOptions.js.map +1 -1
  26. package/dist-esm/src/credentials/chainedTokenCredential.js +1 -1
  27. package/dist-esm/src/credentials/chainedTokenCredential.js.map +1 -1
  28. package/dist-esm/src/credentials/clientAssertionCredential.js +4 -1
  29. package/dist-esm/src/credentials/clientAssertionCredential.js.map +1 -1
  30. package/dist-esm/src/credentials/clientAssertionCredentialOptions.js +4 -0
  31. package/dist-esm/src/credentials/clientAssertionCredentialOptions.js.map +1 -0
  32. package/dist-esm/src/credentials/clientCertificateCredential.js +4 -0
  33. package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
  34. package/dist-esm/src/credentials/clientCertificateCredentialOptions.js.map +1 -1
  35. package/dist-esm/src/credentials/clientSecretCredential.browser.js +6 -3
  36. package/dist-esm/src/credentials/clientSecretCredential.browser.js.map +1 -1
  37. package/dist-esm/src/credentials/clientSecretCredential.js +6 -1
  38. package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
  39. package/dist-esm/src/credentials/clientSecretCredentialOptions.js.map +1 -1
  40. package/dist-esm/src/credentials/defaultAzureCredential.browser.js.map +1 -1
  41. package/dist-esm/src/credentials/defaultAzureCredential.js +2 -2
  42. package/dist-esm/src/credentials/defaultAzureCredential.js.map +1 -1
  43. package/dist-esm/src/credentials/defaultAzureCredentialOptions.js +4 -0
  44. package/dist-esm/src/credentials/defaultAzureCredentialOptions.js.map +1 -0
  45. package/dist-esm/src/credentials/deviceCodeCredential.js +7 -2
  46. package/dist-esm/src/credentials/deviceCodeCredential.js.map +1 -1
  47. package/dist-esm/src/credentials/deviceCodeCredentialOptions.js.map +1 -1
  48. package/dist-esm/src/credentials/environmentCredential.browser.js.map +1 -1
  49. package/dist-esm/src/credentials/environmentCredential.js +18 -7
  50. package/dist-esm/src/credentials/environmentCredential.js.map +1 -1
  51. package/dist-esm/src/credentials/environmentCredentialOptions.js +4 -0
  52. package/dist-esm/src/credentials/environmentCredentialOptions.js.map +1 -0
  53. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js +8 -2
  54. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js.map +1 -1
  55. package/dist-esm/src/credentials/interactiveBrowserCredential.js +8 -3
  56. package/dist-esm/src/credentials/interactiveBrowserCredential.js.map +1 -1
  57. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js.map +1 -1
  58. package/dist-esm/src/credentials/interactiveCredentialOptions.js.map +1 -1
  59. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js +1 -8
  60. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js.map +1 -1
  61. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2019.js +1 -8
  62. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2019.js.map +1 -1
  63. package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js +1 -8
  64. package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js.map +1 -1
  65. package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js +1 -18
  66. package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js.map +1 -1
  67. package/dist-esm/src/credentials/managedIdentityCredential/models.js.map +1 -1
  68. package/dist-esm/src/credentials/managedIdentityCredential/utils.js +23 -0
  69. package/dist-esm/src/credentials/managedIdentityCredential/utils.js.map +1 -1
  70. package/dist-esm/src/credentials/multiTenantTokenCredentialOptions.js +4 -0
  71. package/dist-esm/src/credentials/multiTenantTokenCredentialOptions.js.map +1 -0
  72. package/dist-esm/src/credentials/onBehalfOfCredential.js +7 -2
  73. package/dist-esm/src/credentials/onBehalfOfCredential.js.map +1 -1
  74. package/dist-esm/src/credentials/onBehalfOfCredentialOptions.js.map +1 -1
  75. package/dist-esm/src/credentials/usernamePasswordCredential.browser.js +8 -17
  76. package/dist-esm/src/credentials/usernamePasswordCredential.browser.js.map +1 -1
  77. package/dist-esm/src/credentials/usernamePasswordCredential.js +7 -2
  78. package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -1
  79. package/dist-esm/src/credentials/usernamePasswordCredentialOptions.js.map +1 -1
  80. package/dist-esm/src/credentials/visualStudioCodeCredential.browser.js.map +1 -1
  81. package/dist-esm/src/credentials/visualStudioCodeCredential.js +9 -7
  82. package/dist-esm/src/credentials/visualStudioCodeCredential.js.map +1 -1
  83. package/dist-esm/src/credentials/visualStudioCodeCredentialOptions.js +4 -0
  84. package/dist-esm/src/credentials/visualStudioCodeCredentialOptions.js.map +1 -0
  85. package/dist-esm/src/index.js +4 -4
  86. package/dist-esm/src/index.js.map +1 -1
  87. package/dist-esm/src/msal/browserFlows/msalAuthCode.js +2 -2
  88. package/dist-esm/src/msal/browserFlows/msalAuthCode.js.map +1 -1
  89. package/dist-esm/src/msal/browserFlows/msalBrowserCommon.js +2 -3
  90. package/dist-esm/src/msal/browserFlows/msalBrowserCommon.js.map +1 -1
  91. package/dist-esm/src/msal/credentials.js.map +1 -1
  92. package/dist-esm/src/msal/flows.js.map +1 -1
  93. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js +1 -1
  94. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js.map +1 -1
  95. package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js +1 -1
  96. package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js.map +1 -1
  97. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js +3 -3
  98. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js.map +1 -1
  99. package/dist-esm/src/msal/nodeFlows/msalClientSecret.js.map +1 -1
  100. package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js.map +1 -1
  101. package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js +4 -5
  102. package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js.map +1 -1
  103. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js +1 -1
  104. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js.map +1 -1
  105. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js +4 -4
  106. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js.map +1 -1
  107. package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js.map +1 -1
  108. package/dist-esm/src/msal/utils.js +4 -4
  109. package/dist-esm/src/msal/utils.js.map +1 -1
  110. package/dist-esm/src/plugins/provider.js.map +1 -1
  111. package/dist-esm/src/util/processMultiTenantRequest.browser.js +29 -0
  112. package/dist-esm/src/util/processMultiTenantRequest.browser.js.map +1 -0
  113. package/dist-esm/src/util/processMultiTenantRequest.js +32 -0
  114. package/dist-esm/src/util/processMultiTenantRequest.js.map +1 -0
  115. package/dist-esm/src/util/scopeUtils.js +7 -0
  116. package/dist-esm/src/util/scopeUtils.js.map +1 -1
  117. package/dist-esm/src/util/tenantIdUtils.js +44 -0
  118. package/dist-esm/src/util/tenantIdUtils.js.map +1 -0
  119. package/dist-esm/src/util/tracing.js +1 -1
  120. package/dist-esm/src/util/tracing.js.map +1 -1
  121. package/package.json +2 -2
  122. package/types/identity.d.ts +59 -15
  123. package/dist-esm/src/util/checkTenantId.js +0 -11
  124. package/dist-esm/src/util/checkTenantId.js.map +0 -1
  125. package/dist-esm/src/util/resolveTenantId.js +0 -18
  126. package/dist-esm/src/util/resolveTenantId.js.map +0 -1
  127. package/dist-esm/src/util/validateMultiTenant.browser.js +0 -22
  128. package/dist-esm/src/util/validateMultiTenant.browser.js.map +0 -1
  129. package/dist-esm/src/util/validateMultiTenant.js +0 -29
  130. package/dist-esm/src/util/validateMultiTenant.js.map +0 -1
package/dist/index.js CHANGED
@@ -3,21 +3,21 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var msalNode = require('@azure/msal-node');
6
- var coreClient = require('@azure/core-client');
6
+ var msalCommon = require('@azure/msal-common');
7
+ var logger$m = require('@azure/logger');
8
+ var abortController = require('@azure/abort-controller');
7
9
  var coreUtil = require('@azure/core-util');
10
+ var uuid = require('uuid');
11
+ var coreClient = require('@azure/core-client');
8
12
  var coreRestPipeline = require('@azure/core-rest-pipeline');
9
- var abortController = require('@azure/abort-controller');
10
13
  var coreTracing = require('@azure/core-tracing');
11
- var logger$m = require('@azure/logger');
12
- var msalCommon = require('@azure/msal-common');
13
- var uuid = require('uuid');
14
14
  var fs = require('fs');
15
15
  var os = require('os');
16
16
  var path = require('path');
17
- var child_process = require('child_process');
18
- var crypto = require('crypto');
19
17
  var util = require('util');
20
18
  var https = require('https');
19
+ var child_process = require('child_process');
20
+ var crypto = require('crypto');
21
21
  var http = require('http');
22
22
  var open = require('open');
23
23
  var stoppable = require('stoppable');
@@ -47,9 +47,9 @@ var msalCommon__namespace = /*#__PURE__*/_interopNamespace(msalCommon);
47
47
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
48
48
  var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
49
49
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
50
+ var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
50
51
  var child_process__default = /*#__PURE__*/_interopDefaultLegacy(child_process);
51
52
  var child_process__namespace = /*#__PURE__*/_interopNamespace(child_process);
52
- var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
53
53
  var http__default = /*#__PURE__*/_interopDefaultLegacy(http);
54
54
  var open__default = /*#__PURE__*/_interopDefaultLegacy(open);
55
55
  var stoppable__default = /*#__PURE__*/_interopDefaultLegacy(stoppable);
@@ -173,74 +173,6 @@ class AuthenticationRequiredError extends Error {
173
173
  }
174
174
  }
175
175
 
176
- // Copyright (c) Microsoft Corporation.
177
- // Licensed under the MIT license.
178
- function getIdentityTokenEndpointSuffix(tenantId) {
179
- if (tenantId === "adfs") {
180
- return "oauth2/token";
181
- }
182
- else {
183
- return "oauth2/v2.0/token";
184
- }
185
- }
186
-
187
- // Copyright (c) Microsoft Corporation.
188
- // Licensed under the MIT license.
189
- /**
190
- * Current version of the `@azure/identity` package.
191
- */
192
- const SDK_VERSION = `3.0.0-beta.1`;
193
- /**
194
- * The default client ID for authentication
195
- * @internal
196
- */
197
- // TODO: temporary - this is the Azure CLI clientID - we'll replace it when
198
- // Developer Sign On application is available
199
- // https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/Constants.cs#L9
200
- const DeveloperSignOnClientId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
201
- /**
202
- * The default tenant for authentication
203
- * @internal
204
- */
205
- const DefaultTenantId = "common";
206
- /**
207
- * A list of known Azure authority hosts
208
- */
209
- exports.AzureAuthorityHosts = void 0;
210
- (function (AzureAuthorityHosts) {
211
- /**
212
- * China-based Azure Authority Host
213
- */
214
- AzureAuthorityHosts["AzureChina"] = "https://login.chinacloudapi.cn";
215
- /**
216
- * Germany-based Azure Authority Host
217
- */
218
- AzureAuthorityHosts["AzureGermany"] = "https://login.microsoftonline.de";
219
- /**
220
- * US Government Azure Authority Host
221
- */
222
- AzureAuthorityHosts["AzureGovernment"] = "https://login.microsoftonline.us";
223
- /**
224
- * Public Cloud Azure Authority Host
225
- */
226
- AzureAuthorityHosts["AzurePublicCloud"] = "https://login.microsoftonline.com";
227
- })(exports.AzureAuthorityHosts || (exports.AzureAuthorityHosts = {}));
228
- /**
229
- * The default authority host.
230
- */
231
- const DefaultAuthorityHost = exports.AzureAuthorityHosts.AzurePublicCloud;
232
-
233
- // Copyright (c) Microsoft Corporation.
234
- /**
235
- * Creates a span using the global tracer.
236
- * @internal
237
- */
238
- const tracingClient = coreTracing.createTracingClient({
239
- namespace: "Microsoft.AAD",
240
- packageName: "@azure/identity",
241
- packageVersion: SDK_VERSION,
242
- });
243
-
244
176
  // Copyright (c) Microsoft Corporation.
245
177
  /**
246
178
  * The AzureLogger used for all clients within the identity package
@@ -320,65 +252,402 @@ function credentialLogger(title, log = logger$l) {
320
252
  }
321
253
 
322
254
  // Copyright (c) Microsoft Corporation.
323
- const noCorrelationId = "noCorrelationId";
255
+ // Licensed under the MIT license.
256
+ /**
257
+ * Current version of the `@azure/identity` package.
258
+ */
259
+ const SDK_VERSION = `3.0.0-beta.1`;
324
260
  /**
261
+ * The default client ID for authentication
325
262
  * @internal
326
263
  */
327
- function getIdentityClientAuthorityHost(options) {
328
- // The authorityHost can come from options or from the AZURE_AUTHORITY_HOST environment variable.
329
- let authorityHost = options === null || options === void 0 ? void 0 : options.authorityHost;
330
- // The AZURE_AUTHORITY_HOST environment variable can only be provided in Node.js.
331
- if (coreUtil.isNode) {
332
- authorityHost = authorityHost !== null && authorityHost !== void 0 ? authorityHost : process.env.AZURE_AUTHORITY_HOST;
264
+ // TODO: temporary - this is the Azure CLI clientID - we'll replace it when
265
+ // Developer Sign On application is available
266
+ // https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/Constants.cs#L9
267
+ const DeveloperSignOnClientId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
268
+ /**
269
+ * The default tenant for authentication
270
+ * @internal
271
+ */
272
+ const DefaultTenantId = "common";
273
+ /**
274
+ * A list of known Azure authority hosts
275
+ */
276
+ exports.AzureAuthorityHosts = void 0;
277
+ (function (AzureAuthorityHosts) {
278
+ /**
279
+ * China-based Azure Authority Host
280
+ */
281
+ AzureAuthorityHosts["AzureChina"] = "https://login.chinacloudapi.cn";
282
+ /**
283
+ * Germany-based Azure Authority Host
284
+ */
285
+ AzureAuthorityHosts["AzureGermany"] = "https://login.microsoftonline.de";
286
+ /**
287
+ * US Government Azure Authority Host
288
+ */
289
+ AzureAuthorityHosts["AzureGovernment"] = "https://login.microsoftonline.us";
290
+ /**
291
+ * Public Cloud Azure Authority Host
292
+ */
293
+ AzureAuthorityHosts["AzurePublicCloud"] = "https://login.microsoftonline.com";
294
+ })(exports.AzureAuthorityHosts || (exports.AzureAuthorityHosts = {}));
295
+ /**
296
+ * The default authority host.
297
+ */
298
+ const DefaultAuthorityHost = exports.AzureAuthorityHosts.AzurePublicCloud;
299
+ /**
300
+ * Allow acquiring tokens for any tenant for multi-tentant auth.
301
+ */
302
+ const ALL_TENANTS = ["*"];
303
+
304
+ // Copyright (c) Microsoft Corporation.
305
+ /**
306
+ * Latest AuthenticationRecord version
307
+ * @internal
308
+ */
309
+ const LatestAuthenticationRecordVersion = "1.0";
310
+ /**
311
+ * Ensures the validity of the MSAL token
312
+ * @internal
313
+ */
314
+ function ensureValidMsalToken(scopes, logger, msalToken, getTokenOptions) {
315
+ const error = (message) => {
316
+ logger.getToken.info(message);
317
+ return new AuthenticationRequiredError({
318
+ scopes: Array.isArray(scopes) ? scopes : [scopes],
319
+ getTokenOptions,
320
+ message,
321
+ });
322
+ };
323
+ if (!msalToken) {
324
+ throw error("No response");
325
+ }
326
+ if (!msalToken.expiresOn) {
327
+ throw error(`Response had no "expiresOn" property.`);
328
+ }
329
+ if (!msalToken.accessToken) {
330
+ throw error(`Response had no "accessToken" property.`);
333
331
  }
334
- // If the authorityHost is not provided, we use the default one from the public cloud: https://login.microsoftonline.com
335
- return authorityHost !== null && authorityHost !== void 0 ? authorityHost : DefaultAuthorityHost;
336
332
  }
337
333
  /**
338
- * The network module used by the Identity credentials.
334
+ * Generates a valid authority by combining a host with a tenantId.
335
+ * @internal
336
+ */
337
+ function getAuthority(tenantId, host) {
338
+ if (!host) {
339
+ host = DefaultAuthorityHost;
340
+ }
341
+ if (new RegExp(`${tenantId}/?$`).test(host)) {
342
+ return host;
343
+ }
344
+ if (host.endsWith("/")) {
345
+ return host + tenantId;
346
+ }
347
+ else {
348
+ return `${host}/${tenantId}`;
349
+ }
350
+ }
351
+ /**
352
+ * Generates the known authorities.
353
+ * If the Tenant Id is `adfs`, the authority can't be validated since the format won't match the expected one.
354
+ * For that reason, we have to force MSAL to disable validating the authority
355
+ * by sending it within the known authorities in the MSAL configuration.
356
+ * @internal
357
+ */
358
+ function getKnownAuthorities(tenantId, authorityHost) {
359
+ if (tenantId === "adfs" && authorityHost) {
360
+ return [authorityHost];
361
+ }
362
+ return [];
363
+ }
364
+ /**
365
+ * Generates a logger that can be passed to the MSAL clients.
366
+ * @param logger - The logger of the credential.
367
+ * @internal
368
+ */
369
+ const defaultLoggerCallback = (logger, platform = coreUtil.isNode ? "Node" : "Browser") => (level, message, containsPii) => {
370
+ if (containsPii) {
371
+ return;
372
+ }
373
+ switch (level) {
374
+ case msalCommon__namespace.LogLevel.Error:
375
+ logger.info(`MSAL ${platform} V2 error: ${message}`);
376
+ return;
377
+ case msalCommon__namespace.LogLevel.Info:
378
+ logger.info(`MSAL ${platform} V2 info message: ${message}`);
379
+ return;
380
+ case msalCommon__namespace.LogLevel.Verbose:
381
+ logger.info(`MSAL ${platform} V2 verbose message: ${message}`);
382
+ return;
383
+ case msalCommon__namespace.LogLevel.Warning:
384
+ logger.info(`MSAL ${platform} V2 warning: ${message}`);
385
+ return;
386
+ }
387
+ };
388
+ /**
389
+ * The common utility functions for the MSAL clients.
390
+ * Defined as a class so that the classes extending this one can have access to its methods and protected properties.
339
391
  *
340
- * It allows for credentials to abort any pending request independently of the MSAL flow,
341
- * by calling to the `abortRequests()` method.
392
+ * It keeps track of a logger and an in-memory copy of the AuthenticationRecord.
342
393
  *
394
+ * @internal
343
395
  */
344
- class IdentityClient extends coreClient.ServiceClient {
396
+ class MsalBaseUtilities {
345
397
  constructor(options) {
346
- var _a, _b;
347
- const packageDetails = `azsdk-js-identity/${SDK_VERSION}`;
348
- const userAgentPrefix = ((_a = options === null || options === void 0 ? void 0 : options.userAgentOptions) === null || _a === void 0 ? void 0 : _a.userAgentPrefix)
349
- ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}`
350
- : `${packageDetails}`;
351
- const baseUri = getIdentityClientAuthorityHost(options);
352
- if (!baseUri.startsWith("https:")) {
353
- throw new Error("The authorityHost address must use the 'https' protocol.");
354
- }
355
- super(Object.assign(Object.assign({ requestContentType: "application/json; charset=utf-8", retryOptions: {
356
- maxRetries: 3,
357
- } }, options), { userAgentOptions: {
358
- userAgentPrefix,
359
- }, baseUri }));
360
- this.authorityHost = baseUri;
361
- this.abortControllers = new Map();
362
- this.allowLoggingAccountIdentifiers = (_b = options === null || options === void 0 ? void 0 : options.loggingOptions) === null || _b === void 0 ? void 0 : _b.allowLoggingAccountIdentifiers;
398
+ this.logger = options.logger;
399
+ this.account = options.authenticationRecord;
363
400
  }
364
- async sendTokenRequest(request, expiresOnParser) {
365
- logger$l.info(`IdentityClient: sending token request to [${request.url}]`);
366
- const response = await this.sendRequest(request);
367
- expiresOnParser =
368
- expiresOnParser ||
369
- ((responseBody) => {
370
- return Date.now() + responseBody.expires_in * 1000;
371
- });
372
- if (response.bodyAsText && (response.status === 200 || response.status === 201)) {
373
- const parsedBody = JSON.parse(response.bodyAsText);
374
- if (!parsedBody.access_token) {
375
- return null;
376
- }
377
- this.logIdentifiers(response);
401
+ /**
402
+ * Generates a UUID
403
+ */
404
+ generateUuid() {
405
+ return uuid.v4();
406
+ }
407
+ /**
408
+ * Handles the MSAL authentication result.
409
+ * If the result has an account, we update the local account reference.
410
+ * If the token received is invalid, an error will be thrown depending on what's missing.
411
+ */
412
+ handleResult(scopes, clientId, result, getTokenOptions) {
413
+ if (result === null || result === void 0 ? void 0 : result.account) {
414
+ this.account = msalToPublic(clientId, result.account);
415
+ }
416
+ ensureValidMsalToken(scopes, this.logger, result, getTokenOptions);
417
+ this.logger.getToken.info(formatSuccess(scopes));
418
+ return {
419
+ token: result.accessToken,
420
+ expiresOnTimestamp: result.expiresOn.getTime(),
421
+ };
422
+ }
423
+ /**
424
+ * Handles MSAL errors.
425
+ */
426
+ handleError(scopes, error, getTokenOptions) {
427
+ if (error.name === "AuthError" ||
428
+ error.name === "ClientAuthError" ||
429
+ error.name === "BrowserAuthError") {
430
+ const msalError = error;
431
+ switch (msalError.errorCode) {
432
+ case "endpoints_resolution_error":
433
+ this.logger.info(formatError(scopes, error.message));
434
+ return new CredentialUnavailableError(error.message);
435
+ case "device_code_polling_cancelled":
436
+ return new abortController.AbortError("The authentication has been aborted by the caller.");
437
+ case "consent_required":
438
+ case "interaction_required":
439
+ case "login_required":
440
+ this.logger.info(formatError(scopes, `Authentication returned errorCode ${msalError.errorCode}`));
441
+ break;
442
+ default:
443
+ this.logger.info(formatError(scopes, `Failed to acquire token: ${error.message}`));
444
+ break;
445
+ }
446
+ }
447
+ if (error.name === "ClientConfigurationError" ||
448
+ error.name === "BrowserConfigurationAuthError" ||
449
+ error.name === "AbortError") {
450
+ return error;
451
+ }
452
+ return new AuthenticationRequiredError({ scopes, getTokenOptions, message: error.message });
453
+ }
454
+ }
455
+ // transformations.ts
456
+ function publicToMsal(account) {
457
+ const [environment] = account.authority.match(/([a-z]*\.[a-z]*\.[a-z]*)/) || [];
458
+ return Object.assign(Object.assign({}, account), { localAccountId: account.homeAccountId, environment });
459
+ }
460
+ function msalToPublic(clientId, account) {
461
+ const record = {
462
+ authority: getAuthority(account.tenantId, account.environment),
463
+ homeAccountId: account.homeAccountId,
464
+ tenantId: account.tenantId || DefaultTenantId,
465
+ username: account.username,
466
+ clientId,
467
+ version: LatestAuthenticationRecordVersion,
468
+ };
469
+ return record;
470
+ }
471
+ /**
472
+ * Serializes an `AuthenticationRecord` into a string.
473
+ *
474
+ * The output of a serialized authentication record will contain the following properties:
475
+ *
476
+ * - "authority"
477
+ * - "homeAccountId"
478
+ * - "clientId"
479
+ * - "tenantId"
480
+ * - "username"
481
+ * - "version"
482
+ *
483
+ * To later convert this string to a serialized `AuthenticationRecord`, please use the exported function `deserializeAuthenticationRecord()`.
484
+ */
485
+ function serializeAuthenticationRecord(record) {
486
+ return JSON.stringify(record);
487
+ }
488
+ /**
489
+ * Deserializes a previously serialized authentication record from a string into an object.
490
+ *
491
+ * The input string must contain the following properties:
492
+ *
493
+ * - "authority"
494
+ * - "homeAccountId"
495
+ * - "clientId"
496
+ * - "tenantId"
497
+ * - "username"
498
+ * - "version"
499
+ *
500
+ * If the version we receive is unsupported, an error will be thrown.
501
+ *
502
+ * At the moment, the only available version is: "1.0", which is always set when the authentication record is serialized.
503
+ *
504
+ * @param serializedRecord - Authentication record previously serialized into string.
505
+ * @returns AuthenticationRecord.
506
+ */
507
+ function deserializeAuthenticationRecord(serializedRecord) {
508
+ const parsed = JSON.parse(serializedRecord);
509
+ if (parsed.version && parsed.version !== LatestAuthenticationRecordVersion) {
510
+ throw Error("Unsupported AuthenticationRecord version");
511
+ }
512
+ return parsed;
513
+ }
514
+
515
+ // Copyright (c) Microsoft Corporation.
516
+ // Licensed under the MIT license.
517
+ function getIdentityTokenEndpointSuffix(tenantId) {
518
+ if (tenantId === "adfs") {
519
+ return "oauth2/token";
520
+ }
521
+ else {
522
+ return "oauth2/v2.0/token";
523
+ }
524
+ }
525
+
526
+ // Copyright (c) Microsoft Corporation.
527
+ /**
528
+ * Creates a span using the global tracer.
529
+ * @internal
530
+ */
531
+ const tracingClient = coreTracing.createTracingClient({
532
+ namespace: "Microsoft.AAD",
533
+ packageName: "@azure/identity",
534
+ packageVersion: SDK_VERSION,
535
+ });
536
+
537
+ // Copyright (c) Microsoft Corporation.
538
+ // Licensed under the MIT license.
539
+ const DefaultScopeSuffix = "/.default";
540
+ const imdsHost = "http://169.254.169.254";
541
+ const imdsEndpointPath = "/metadata/identity/oauth2/token";
542
+ const imdsApiVersion = "2018-02-01";
543
+ const azureArcAPIVersion = "2019-11-01";
544
+ const azureFabricVersion = "2019-07-01-preview";
545
+
546
+ // Copyright (c) Microsoft Corporation.
547
+ /**
548
+ * Most MSIs send requests to the IMDS endpoint, or a similar endpoint.
549
+ * These are GET requests that require sending a `resource` parameter on the query.
550
+ * This resource can be derived from the scopes received through the getToken call, as long as only one scope is received.
551
+ * Multiple scopes assume that the resulting token will have access to multiple resources, which won't be the case.
552
+ *
553
+ * For that reason, when we encounter multiple scopes, we return undefined.
554
+ * It's up to the individual MSI implementations to throw the errors (which helps us provide less generic errors).
555
+ */
556
+ function mapScopesToResource(scopes) {
557
+ let scope = "";
558
+ if (Array.isArray(scopes)) {
559
+ if (scopes.length !== 1) {
560
+ return;
561
+ }
562
+ scope = scopes[0];
563
+ }
564
+ else if (typeof scopes === "string") {
565
+ scope = scopes;
566
+ }
567
+ if (!scope.endsWith(DefaultScopeSuffix)) {
568
+ return scope;
569
+ }
570
+ return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix));
571
+ }
572
+ /**
573
+ * Given a token response, return the expiration timestamp as the number of milliseconds from the Unix epoch.
574
+ * @param body - A parsed response body from the authentication endpoint.
575
+ */
576
+ function parseExpiresOn(body) {
577
+ if (typeof body.expires_on === "number") {
578
+ return body.expires_on * 1000;
579
+ }
580
+ if (typeof body.expires_on === "string") {
581
+ const asNumber = +body.expires_on;
582
+ if (!isNaN(asNumber)) {
583
+ return asNumber * 1000;
584
+ }
585
+ const asDate = Date.parse(body.expires_on);
586
+ if (!isNaN(asDate)) {
587
+ return asDate;
588
+ }
589
+ }
590
+ if (typeof body.expires_in === "number") {
591
+ return Date.now() + body.expires_in * 1000;
592
+ }
593
+ throw new Error(`Failed to parse token expiration from body. expires_in="${body.expires_in}", expires_on="${body.expires_on}"`);
594
+ }
595
+
596
+ // Copyright (c) Microsoft Corporation.
597
+ const noCorrelationId = "noCorrelationId";
598
+ /**
599
+ * @internal
600
+ */
601
+ function getIdentityClientAuthorityHost(options) {
602
+ // The authorityHost can come from options or from the AZURE_AUTHORITY_HOST environment variable.
603
+ let authorityHost = options === null || options === void 0 ? void 0 : options.authorityHost;
604
+ // The AZURE_AUTHORITY_HOST environment variable can only be provided in Node.js.
605
+ if (coreUtil.isNode) {
606
+ authorityHost = authorityHost !== null && authorityHost !== void 0 ? authorityHost : process.env.AZURE_AUTHORITY_HOST;
607
+ }
608
+ // If the authorityHost is not provided, we use the default one from the public cloud: https://login.microsoftonline.com
609
+ return authorityHost !== null && authorityHost !== void 0 ? authorityHost : DefaultAuthorityHost;
610
+ }
611
+ /**
612
+ * The network module used by the Identity credentials.
613
+ *
614
+ * It allows for credentials to abort any pending request independently of the MSAL flow,
615
+ * by calling to the `abortRequests()` method.
616
+ *
617
+ */
618
+ class IdentityClient extends coreClient.ServiceClient {
619
+ constructor(options) {
620
+ var _a, _b;
621
+ const packageDetails = `azsdk-js-identity/${SDK_VERSION}`;
622
+ const userAgentPrefix = ((_a = options === null || options === void 0 ? void 0 : options.userAgentOptions) === null || _a === void 0 ? void 0 : _a.userAgentPrefix)
623
+ ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}`
624
+ : `${packageDetails}`;
625
+ const baseUri = getIdentityClientAuthorityHost(options);
626
+ if (!baseUri.startsWith("https:")) {
627
+ throw new Error("The authorityHost address must use the 'https' protocol.");
628
+ }
629
+ super(Object.assign(Object.assign({ requestContentType: "application/json; charset=utf-8", retryOptions: {
630
+ maxRetries: 3,
631
+ } }, options), { userAgentOptions: {
632
+ userAgentPrefix,
633
+ }, baseUri }));
634
+ this.authorityHost = baseUri;
635
+ this.abortControllers = new Map();
636
+ this.allowLoggingAccountIdentifiers = (_b = options === null || options === void 0 ? void 0 : options.loggingOptions) === null || _b === void 0 ? void 0 : _b.allowLoggingAccountIdentifiers;
637
+ }
638
+ async sendTokenRequest(request) {
639
+ logger$l.info(`IdentityClient: sending token request to [${request.url}]`);
640
+ const response = await this.sendRequest(request);
641
+ if (response.bodyAsText && (response.status === 200 || response.status === 201)) {
642
+ const parsedBody = JSON.parse(response.bodyAsText);
643
+ if (!parsedBody.access_token) {
644
+ return null;
645
+ }
646
+ this.logIdentifiers(response);
378
647
  const token = {
379
648
  accessToken: {
380
649
  token: parsedBody.access_token,
381
- expiresOnTimestamp: expiresOnParser(parsedBody),
650
+ expiresOnTimestamp: parseExpiresOn(parsedBody),
382
651
  },
383
652
  refreshToken: parsedBody.refresh_token,
384
653
  };
@@ -391,7 +660,7 @@ class IdentityClient extends coreClient.ServiceClient {
391
660
  throw error;
392
661
  }
393
662
  }
394
- async refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, expiresOnParser, options = {}) {
663
+ async refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, options = {}) {
395
664
  if (refreshToken === undefined) {
396
665
  return null;
397
666
  }
@@ -420,7 +689,7 @@ class IdentityClient extends coreClient.ServiceClient {
420
689
  }),
421
690
  tracingOptions: updatedOptions.tracingOptions,
422
691
  });
423
- const response = await this.sendTokenRequest(request, expiresOnParser);
692
+ const response = await this.sendTokenRequest(request);
424
693
  logger$l.info(`IdentityClient: refreshed token for client ID: ${clientId}`);
425
694
  return response;
426
695
  }
@@ -545,271 +814,7 @@ class IdentityClient extends coreClient.ServiceClient {
545
814
  }
546
815
 
547
816
  // Copyright (c) Microsoft Corporation.
548
- function checkTenantId(logger, tenantId) {
549
- if (!tenantId.match(/^[0-9a-zA-Z-.:/]+$/)) {
550
- const error = new Error("Invalid tenant id provided. You can locate your tenant id by following the instructions listed here: https://docs.microsoft.com/partner-center/find-ids-and-domain-names.");
551
- logger.info(formatError("", error));
552
- throw error;
553
- }
554
- }
555
-
556
- // Copyright (c) Microsoft Corporation.
557
- function resolveTenantId(logger, tenantId, clientId) {
558
- if (tenantId) {
559
- checkTenantId(logger, tenantId);
560
- return tenantId;
561
- }
562
- if (!clientId) {
563
- clientId = DeveloperSignOnClientId;
564
- }
565
- if (clientId !== DeveloperSignOnClientId) {
566
- return "common";
567
- }
568
- return "organizations";
569
- }
570
-
571
- // Copyright (c) Microsoft Corporation.
572
- /**
573
- * Latest AuthenticationRecord version
574
- * @internal
575
- */
576
- const LatestAuthenticationRecordVersion = "1.0";
577
- /**
578
- * Ensures the validity of the MSAL token
579
- * @internal
580
- */
581
- function ensureValidMsalToken(scopes, logger, msalToken, getTokenOptions) {
582
- const error = (message) => {
583
- logger.getToken.info(message);
584
- return new AuthenticationRequiredError({
585
- scopes: Array.isArray(scopes) ? scopes : [scopes],
586
- getTokenOptions,
587
- message,
588
- });
589
- };
590
- if (!msalToken) {
591
- throw error("No response");
592
- }
593
- if (!msalToken.expiresOn) {
594
- throw error(`Response had no "expiresOn" property.`);
595
- }
596
- if (!msalToken.accessToken) {
597
- throw error(`Response had no "accessToken" property.`);
598
- }
599
- }
600
- /**
601
- * Generates a valid authority by combining a host with a tenantId.
602
- * @internal
603
- */
604
- function getAuthority(tenantId, host) {
605
- if (!host) {
606
- host = DefaultAuthorityHost;
607
- }
608
- if (new RegExp(`${tenantId}/?$`).test(host)) {
609
- return host;
610
- }
611
- if (host.endsWith("/")) {
612
- return host + tenantId;
613
- }
614
- else {
615
- return `${host}/${tenantId}`;
616
- }
617
- }
618
- /**
619
- * Generates the known authorities.
620
- * If the Tenant Id is `adfs`, the authority can't be validated since the format won't match the expected one.
621
- * For that reason, we have to force MSAL to disable validating the authority
622
- * by sending it within the known authorities in the MSAL configuration.
623
- * @internal
624
- */
625
- function getKnownAuthorities(tenantId, authorityHost) {
626
- if (tenantId === "adfs" && authorityHost) {
627
- return [authorityHost];
628
- }
629
- return [];
630
- }
631
- /**
632
- * Generates a logger that can be passed to the MSAL clients.
633
- * @param logger - The logger of the credential.
634
- * @internal
635
- */
636
- const defaultLoggerCallback = (logger, platform = coreUtil.isNode ? "Node" : "Browser") => (level, message, containsPii) => {
637
- if (containsPii) {
638
- return;
639
- }
640
- switch (level) {
641
- case msalCommon__namespace.LogLevel.Error:
642
- logger.info(`MSAL ${platform} V2 error: ${message}`);
643
- return;
644
- case msalCommon__namespace.LogLevel.Info:
645
- logger.info(`MSAL ${platform} V2 info message: ${message}`);
646
- return;
647
- case msalCommon__namespace.LogLevel.Verbose:
648
- logger.info(`MSAL ${platform} V2 verbose message: ${message}`);
649
- return;
650
- case msalCommon__namespace.LogLevel.Warning:
651
- logger.info(`MSAL ${platform} V2 warning: ${message}`);
652
- return;
653
- }
654
- };
655
- /**
656
- * The common utility functions for the MSAL clients.
657
- * Defined as a class so that the classes extending this one can have access to its methods and protected properties.
658
- *
659
- * It keeps track of a logger and an in-memory copy of the AuthenticationRecord.
660
- *
661
- * @internal
662
- */
663
- class MsalBaseUtilities {
664
- constructor(options) {
665
- this.logger = options.logger;
666
- this.account = options.authenticationRecord;
667
- }
668
- /**
669
- * Generates a UUID
670
- */
671
- generateUuid() {
672
- return uuid.v4();
673
- }
674
- /**
675
- * Handles the MSAL authentication result.
676
- * If the result has an account, we update the local account reference.
677
- * If the token received is invalid, an error will be thrown depending on what's missing.
678
- */
679
- handleResult(scopes, clientId, result, getTokenOptions) {
680
- if (result === null || result === void 0 ? void 0 : result.account) {
681
- this.account = msalToPublic(clientId, result.account);
682
- }
683
- ensureValidMsalToken(scopes, this.logger, result, getTokenOptions);
684
- this.logger.getToken.info(formatSuccess(scopes));
685
- return {
686
- token: result.accessToken,
687
- expiresOnTimestamp: result.expiresOn.getTime(),
688
- };
689
- }
690
- /**
691
- * Handles MSAL errors.
692
- */
693
- handleError(scopes, error, getTokenOptions) {
694
- if (error.name === "AuthError" ||
695
- error.name === "ClientAuthError" ||
696
- error.name === "BrowserAuthError") {
697
- const msalError = error;
698
- switch (msalError.errorCode) {
699
- case "endpoints_resolution_error":
700
- this.logger.info(formatError(scopes, error.message));
701
- return new CredentialUnavailableError(error.message);
702
- case "device_code_polling_cancelled":
703
- return new abortController.AbortError("The authentication has been aborted by the caller.");
704
- case "consent_required":
705
- case "interaction_required":
706
- case "login_required":
707
- this.logger.info(formatError(scopes, `Authentication returned errorCode ${msalError.errorCode}`));
708
- break;
709
- default:
710
- this.logger.info(formatError(scopes, `Failed to acquire token: ${error.message}`));
711
- break;
712
- }
713
- }
714
- if (error.name === "ClientConfigurationError" ||
715
- error.name === "BrowserConfigurationAuthError" ||
716
- error.name === "AbortError") {
717
- return error;
718
- }
719
- return new AuthenticationRequiredError({ scopes, getTokenOptions, message: error.message });
720
- }
721
- }
722
- // transformations.ts
723
- function publicToMsal(account) {
724
- const [environment] = account.authority.match(/([a-z]*\.[a-z]*\.[a-z]*)/) || [];
725
- return Object.assign(Object.assign({}, account), { localAccountId: account.homeAccountId, environment });
726
- }
727
- function msalToPublic(clientId, account) {
728
- const record = {
729
- authority: getAuthority(account.tenantId, account.environment),
730
- homeAccountId: account.homeAccountId,
731
- tenantId: account.tenantId || DefaultTenantId,
732
- username: account.username,
733
- clientId,
734
- version: LatestAuthenticationRecordVersion,
735
- };
736
- return record;
737
- }
738
- /**
739
- * Serializes an `AuthenticationRecord` into a string.
740
- *
741
- * The output of a serialized authentication record will contain the following properties:
742
- *
743
- * - "authority"
744
- * - "homeAccountId"
745
- * - "clientId"
746
- * - "tenantId"
747
- * - "username"
748
- * - "version"
749
- *
750
- * To later convert this string to a serialized `AuthenticationRecord`, please use the exported function `deserializeAuthenticationRecord()`.
751
- */
752
- function serializeAuthenticationRecord(record) {
753
- return JSON.stringify(record);
754
- }
755
- /**
756
- * Deserializes a previously serialized authentication record from a string into an object.
757
- *
758
- * The input string must contain the following properties:
759
- *
760
- * - "authority"
761
- * - "homeAccountId"
762
- * - "clientId"
763
- * - "tenantId"
764
- * - "username"
765
- * - "version"
766
- *
767
- * If the version we receive is unsupported, an error will be thrown.
768
- *
769
- * At the moment, the only available version is: "1.0", which is always set when the authentication record is serialized.
770
- *
771
- * @param serializedRecord - Authentication record previously serialized into string.
772
- * @returns AuthenticationRecord.
773
- */
774
- function deserializeAuthenticationRecord(serializedRecord) {
775
- const parsed = JSON.parse(serializedRecord);
776
- if (parsed.version && parsed.version !== LatestAuthenticationRecordVersion) {
777
- throw Error("Unsupported AuthenticationRecord version");
778
- }
779
- return parsed;
780
- }
781
-
782
- // Copyright (c) Microsoft Corporation.
783
- // Licensed under the MIT license.
784
- /**
785
- * @internal
786
- */
787
- const multiTenantDisabledErrorMessage = "A getToken request was attempted with a tenant different than the tenant configured at the initialization of the credential, but multi-tenant authentication has been disabled by the environment variable AZURE_IDENTITY_DISABLE_MULTITENANTAUTH.";
788
- /**
789
- * @internal
790
- */
791
- const multiTenantADFSErrorMessage = "A new tenant Id can't be assigned through the GetTokenOptions when a credential has been originally configured to use the tenant `adfs`.";
792
- /**
793
- * Of getToken contains a tenantId, this functions allows picking this tenantId as the appropriate for authentication,
794
- * unless multitenant authentication has been disabled through the AZURE_IDENTITY_DISABLE_MULTITENANTAUTH (on Node.js),
795
- * or unless the original tenant Id is `adfs`.
796
- * @internal
797
- */
798
- function processMultiTenantRequest(tenantId, getTokenOptions) {
799
- if (!(getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId)) {
800
- return tenantId;
801
- }
802
- if (process.env.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH) {
803
- throw new Error(multiTenantDisabledErrorMessage);
804
- }
805
- if (tenantId === "adfs") {
806
- throw new Error(multiTenantADFSErrorMessage);
807
- }
808
- return getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId;
809
- }
810
-
811
- // Copyright (c) Microsoft Corporation.
812
- // Licensed under the MIT license.
817
+ // Licensed under the MIT license.
813
818
  /**
814
819
  * Helps specify a regional authority, or "AutoDiscoverRegion" to auto-detect the region.
815
820
  */
@@ -924,20 +929,92 @@ var RegionalAuthority;
924
929
  })(RegionalAuthority || (RegionalAuthority = {}));
925
930
 
926
931
  // Copyright (c) Microsoft Corporation.
932
+ // Licensed under the MIT license.
933
+ function createConfigurationErrorMessage(tenantId) {
934
+ return `The current credential is not configured to acquire tokens for tenant ${tenantId}. To enable acquiring tokens for this tenant add it to the AdditionallyAllowedTenants on the credential options, or add "*" to AdditionallyAllowedTenants to allow acquiring tokens for any tenant.`;
935
+ }
927
936
  /**
928
- * The current persistence provider, undefined by default.
937
+ * Of getToken contains a tenantId, this functions allows picking this tenantId as the appropriate for authentication,
938
+ * unless multitenant authentication has been disabled through the AZURE_IDENTITY_DISABLE_MULTITENANTAUTH (on Node.js),
939
+ * or unless the original tenant Id is `adfs`.
929
940
  * @internal
930
941
  */
931
- let persistenceProvider = undefined;
942
+ function processMultiTenantRequest(tenantId, getTokenOptions, additionallyAllowedTenantIds = []) {
943
+ var _a;
944
+ let resolvedTenantId;
945
+ if (process.env.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH) {
946
+ resolvedTenantId = tenantId;
947
+ }
948
+ else if (tenantId === "adfs") {
949
+ resolvedTenantId = tenantId;
950
+ }
951
+ else {
952
+ resolvedTenantId = (_a = getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId) !== null && _a !== void 0 ? _a : tenantId;
953
+ }
954
+ if (tenantId &&
955
+ resolvedTenantId !== tenantId &&
956
+ !additionallyAllowedTenantIds.includes("*") &&
957
+ !additionallyAllowedTenantIds.some((t) => t.localeCompare(resolvedTenantId) === 0)) {
958
+ throw new Error(createConfigurationErrorMessage(tenantId));
959
+ }
960
+ return resolvedTenantId;
961
+ }
962
+
963
+ // Copyright (c) Microsoft Corporation.
932
964
  /**
933
- * An object that allows setting the persistence provider.
934
965
  * @internal
935
966
  */
936
- const msalNodeFlowCacheControl = {
937
- setPersistence(pluginProvider) {
938
- persistenceProvider = pluginProvider;
939
- },
940
- };
967
+ function checkTenantId(logger, tenantId) {
968
+ if (!tenantId.match(/^[0-9a-zA-Z-.:/]+$/)) {
969
+ const error = new Error("Invalid tenant id provided. You can locate your tenant id by following the instructions listed here: https://docs.microsoft.com/partner-center/find-ids-and-domain-names.");
970
+ logger.info(formatError("", error));
971
+ throw error;
972
+ }
973
+ }
974
+ /**
975
+ * @internal
976
+ */
977
+ function resolveTenantId(logger, tenantId, clientId) {
978
+ if (tenantId) {
979
+ checkTenantId(logger, tenantId);
980
+ return tenantId;
981
+ }
982
+ if (!clientId) {
983
+ clientId = DeveloperSignOnClientId;
984
+ }
985
+ if (clientId !== DeveloperSignOnClientId) {
986
+ return "common";
987
+ }
988
+ return "organizations";
989
+ }
990
+ /**
991
+ * @internal
992
+ */
993
+ function resolveAddionallyAllowedTenantIds(additionallyAllowedTenants) {
994
+ if (!additionallyAllowedTenants || additionallyAllowedTenants.length === 0) {
995
+ return [];
996
+ }
997
+ if (additionallyAllowedTenants.includes("*")) {
998
+ return ALL_TENANTS;
999
+ }
1000
+ return additionallyAllowedTenants;
1001
+ }
1002
+
1003
+ // Copyright (c) Microsoft Corporation.
1004
+ /**
1005
+ * The current persistence provider, undefined by default.
1006
+ * @internal
1007
+ */
1008
+ let persistenceProvider = undefined;
1009
+ /**
1010
+ * An object that allows setting the persistence provider.
1011
+ * @internal
1012
+ */
1013
+ const msalNodeFlowCacheControl = {
1014
+ setPersistence(pluginProvider) {
1015
+ persistenceProvider = pluginProvider;
1016
+ },
1017
+ };
941
1018
  /**
942
1019
  * MSAL partial base client for Node.js.
943
1020
  *
@@ -1243,6 +1320,7 @@ class VisualStudioCodeCredential {
1243
1320
  else {
1244
1321
  this.tenantId = CommonTenantId;
1245
1322
  }
1323
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
1246
1324
  checkUnsupportedTenant(this.tenantId);
1247
1325
  }
1248
1326
  /**
@@ -1276,7 +1354,8 @@ class VisualStudioCodeCredential {
1276
1354
  async getToken(scopes, options) {
1277
1355
  var _a, _b;
1278
1356
  await this.prepareOnce();
1279
- const tenantId = processMultiTenantRequest(this.tenantId, options) || this.tenantId;
1357
+ const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds) ||
1358
+ this.tenantId;
1280
1359
  if (findCredentials === undefined) {
1281
1360
  throw new CredentialUnavailableError([
1282
1361
  "No implementation of `VisualStudioCodeCredential` is available.",
@@ -1369,1706 +1448,1655 @@ function useIdentityPlugin(plugin) {
1369
1448
  }
1370
1449
 
1371
1450
  // Copyright (c) Microsoft Corporation.
1451
+ const msiName$6 = "ManagedIdentityCredential - AppServiceMSI 2017";
1452
+ const logger$j = credentialLogger(msiName$6);
1372
1453
  /**
1373
- * @internal
1374
- */
1375
- const logger$j = credentialLogger("ChainedTokenCredential");
1376
- /**
1377
- * Enables multiple `TokenCredential` implementations to be tried in order
1378
- * until one of the getToken methods returns an access token.
1454
+ * Generates the options used on the request for an access token.
1379
1455
  */
1380
- class ChainedTokenCredential {
1381
- /**
1382
- * Creates an instance of ChainedTokenCredential using the given credentials.
1383
- *
1384
- * @param sources - `TokenCredential` implementations to be tried in order.
1385
- *
1386
- * Example usage:
1387
- * ```javascript
1388
- * const firstCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
1389
- * const secondCredential = new ClientSecretCredential(tenantId, anotherClientId, anotherSecret);
1390
- * const credentialChain = new ChainedTokenCredential(firstCredential, secondCredential);
1391
- * ```
1392
- */
1393
- constructor(...sources) {
1394
- /**
1395
- * The message to use when the chained token fails to get a token
1396
- */
1397
- this.UnavailableMessage = "ChainedTokenCredential => failed to retrieve a token from the included credentials";
1398
- this._sources = [];
1399
- this._sources = sources;
1456
+ function prepareRequestOptions$6(scopes, clientId) {
1457
+ const resource = mapScopesToResource(scopes);
1458
+ if (!resource) {
1459
+ throw new Error(`${msiName$6}: Multiple scopes are not supported.`);
1400
1460
  }
1401
- /**
1402
- * Returns the first access token returned by one of the chained
1403
- * `TokenCredential` implementations. Throws an {@link AggregateAuthenticationError}
1404
- * when one or more credentials throws an {@link AuthenticationError} and
1405
- * no credentials have returned an access token.
1406
- *
1407
- * This method is called automatically by Azure SDK client libraries. You may call this method
1408
- * directly, but you must also handle token caching and token refreshing.
1409
- *
1410
- * @param scopes - The list of scopes for which the token will have access.
1411
- * @param options - The options used to configure any requests this
1412
- * `TokenCredential` implementation might make.
1413
- */
1414
- async getToken(scopes, options = {}) {
1415
- let token = null;
1416
- let successfulCredentialName = "";
1417
- const errors = [];
1418
- return tracingClient.withSpan("ChainedTokenCredential.getToken", options, async (updatedOptions) => {
1419
- for (let i = 0; i < this._sources.length && token === null; i++) {
1420
- try {
1421
- token = await this._sources[i].getToken(scopes, updatedOptions);
1422
- successfulCredentialName = this._sources[i].constructor.name;
1423
- }
1424
- catch (err) {
1425
- if (err.name === "CredentialUnavailableError" ||
1426
- err.name === "AuthenticationRequiredError") {
1427
- errors.push(err);
1428
- }
1429
- else {
1430
- logger$j.getToken.info(formatError(scopes, err));
1431
- throw err;
1432
- }
1433
- }
1434
- }
1435
- if (!token && errors.length > 0) {
1436
- const err = new AggregateAuthenticationError(errors, "ChainedTokenCredential authentication failed.");
1437
- logger$j.getToken.info(formatError(scopes, err));
1438
- throw err;
1439
- }
1440
- logger$j.getToken.info(`Result for ${successfulCredentialName}: ${formatSuccess(scopes)}`);
1441
- if (token === null) {
1442
- throw new CredentialUnavailableError("Failed to retrieve a valid token");
1443
- }
1444
- return token;
1445
- });
1461
+ const queryParameters = {
1462
+ resource,
1463
+ "api-version": "2017-09-01",
1464
+ };
1465
+ if (clientId) {
1466
+ queryParameters.clientid = clientId;
1446
1467
  }
1447
- }
1448
-
1449
- // Copyright (c) Microsoft Corporation.
1450
- /**
1451
- * Throws if the received scope is not valid.
1452
- * @internal
1453
- */
1454
- function ensureValidScope(scope, logger) {
1455
- if (!scope.match(/^[0-9a-zA-Z-.:/]+$/)) {
1456
- const error = new Error("Invalid scope was specified by the user or calling client");
1457
- logger.getToken.info(formatError(scope, error));
1458
- throw error;
1468
+ const query = new URLSearchParams(queryParameters);
1469
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
1470
+ if (!process.env.MSI_ENDPOINT) {
1471
+ throw new Error(`${msiName$6}: Missing environment variable: MSI_ENDPOINT`);
1459
1472
  }
1473
+ if (!process.env.MSI_SECRET) {
1474
+ throw new Error(`${msiName$6}: Missing environment variable: MSI_SECRET`);
1475
+ }
1476
+ return {
1477
+ url: `${process.env.MSI_ENDPOINT}?${query.toString()}`,
1478
+ method: "GET",
1479
+ headers: coreRestPipeline.createHttpHeaders({
1480
+ Accept: "application/json",
1481
+ secret: process.env.MSI_SECRET,
1482
+ }),
1483
+ };
1460
1484
  }
1461
1485
  /**
1462
- * Returns the resource out of a scope.
1463
- * @internal
1464
- */
1465
- function getScopeResource(scope) {
1466
- return scope.replace(/\/.default$/, "");
1467
- }
1468
-
1469
- // Copyright (c) Microsoft Corporation.
1470
- /**
1471
- * Mockable reference to the CLI credential cliCredentialFunctions
1472
- * @internal
1486
+ * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
1473
1487
  */
1474
- const cliCredentialInternals = {
1475
- /**
1476
- * @internal
1477
- */
1478
- getSafeWorkingDir() {
1479
- if (process.platform === "win32") {
1480
- if (!process.env.SystemRoot) {
1481
- throw new Error("Azure CLI credential expects a 'SystemRoot' environment variable");
1482
- }
1483
- return process.env.SystemRoot;
1488
+ const appServiceMsi2017 = {
1489
+ async isAvailable({ scopes }) {
1490
+ const resource = mapScopesToResource(scopes);
1491
+ if (!resource) {
1492
+ logger$j.info(`${msiName$6}: Unavailable. Multiple scopes are not supported.`);
1493
+ return false;
1484
1494
  }
1485
- else {
1486
- return "/bin";
1495
+ const env = process.env;
1496
+ const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
1497
+ if (!result) {
1498
+ logger$j.info(`${msiName$6}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`);
1487
1499
  }
1500
+ return result;
1488
1501
  },
1489
- /**
1490
- * Gets the access token from Azure CLI
1491
- * @param resource - The resource to use when getting the token
1492
- * @internal
1493
- */
1494
- async getAzureCliAccessToken(resource, tenantId) {
1495
- let tenantSection = [];
1496
- if (tenantId) {
1497
- tenantSection = ["--tenant", tenantId];
1502
+ async getToken(configuration, getTokenOptions = {}) {
1503
+ const { identityClient, scopes, clientId, resourceId } = configuration;
1504
+ if (resourceId) {
1505
+ logger$j.warning(`${msiName$6}: managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
1498
1506
  }
1499
- return new Promise((resolve, reject) => {
1500
- try {
1501
- child_process__default["default"].execFile("az", [
1502
- "account",
1503
- "get-access-token",
1504
- "--output",
1505
- "json",
1506
- "--resource",
1507
- resource,
1508
- ...tenantSection,
1509
- ], { cwd: cliCredentialInternals.getSafeWorkingDir(), shell: true }, (error, stdout, stderr) => {
1510
- resolve({ stdout: stdout, stderr: stderr, error });
1511
- });
1512
- }
1513
- catch (err) {
1514
- reject(err);
1515
- }
1516
- });
1507
+ logger$j.info(`${msiName$6}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
1508
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$6(scopes, clientId)), {
1509
+ // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
1510
+ allowInsecureConnection: true }));
1511
+ const tokenResponse = await identityClient.sendTokenRequest(request);
1512
+ return (tokenResponse && tokenResponse.accessToken) || null;
1517
1513
  },
1518
1514
  };
1519
- const logger$i = credentialLogger("AzureCliCredential");
1515
+
1516
+ // Copyright (c) Microsoft Corporation.
1517
+ const msiName$5 = "ManagedIdentityCredential - CloudShellMSI";
1518
+ const logger$i = credentialLogger(msiName$5);
1520
1519
  /**
1521
- * This credential will use the currently logged-in user login information
1522
- * via the Azure CLI ('az') commandline tool.
1523
- * To do so, it will read the user access token and expire time
1524
- * with Azure CLI command "az account get-access-token".
1520
+ * Generates the options used on the request for an access token.
1525
1521
  */
1526
- class AzureCliCredential {
1527
- /**
1528
- * Creates an instance of the {@link AzureCliCredential}.
1529
- *
1530
- * To use this credential, ensure that you have already logged
1531
- * in via the 'az' tool using the command "az login" from the commandline.
1532
- *
1533
- * @param options - Options, to optionally allow multi-tenant requests.
1534
- */
1535
- constructor(options) {
1536
- this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
1522
+ function prepareRequestOptions$5(scopes, clientId, resourceId) {
1523
+ const resource = mapScopesToResource(scopes);
1524
+ if (!resource) {
1525
+ throw new Error(`${msiName$5}: Multiple scopes are not supported.`);
1537
1526
  }
1538
- /**
1539
- * Authenticates with Azure Active Directory and returns an access token if successful.
1540
- * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
1541
- *
1542
- * @param scopes - The list of scopes for which the token will have access.
1543
- * @param options - The options used to configure any requests this
1544
- * TokenCredential implementation might make.
1545
- */
1546
- async getToken(scopes, options = {}) {
1547
- const tenantId = processMultiTenantRequest(this.tenantId, options);
1548
- if (tenantId) {
1549
- checkTenantId(logger$i, tenantId);
1550
- }
1551
- const scope = typeof scopes === "string" ? scopes : scopes[0];
1552
- logger$i.getToken.info(`Using the scope ${scope}`);
1553
- ensureValidScope(scope, logger$i);
1554
- const resource = getScopeResource(scope);
1555
- return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => {
1556
- var _a, _b, _c, _d;
1557
- try {
1558
- const obj = await cliCredentialInternals.getAzureCliAccessToken(resource, tenantId);
1559
- const specificScope = (_a = obj.stderr) === null || _a === void 0 ? void 0 : _a.match("(.*)az login --scope(.*)");
1560
- const isLoginError = ((_b = obj.stderr) === null || _b === void 0 ? void 0 : _b.match("(.*)az login(.*)")) && !specificScope;
1561
- const isNotInstallError = ((_c = obj.stderr) === null || _c === void 0 ? void 0 : _c.match("az:(.*)not found")) || ((_d = obj.stderr) === null || _d === void 0 ? void 0 : _d.startsWith("'az' is not recognized"));
1562
- if (isNotInstallError) {
1563
- const error = new CredentialUnavailableError("Azure CLI could not be found. Please visit https://aka.ms/azure-cli for installation instructions and then, once installed, authenticate to your Azure account using 'az login'.");
1564
- logger$i.getToken.info(formatError(scopes, error));
1565
- throw error;
1566
- }
1567
- if (isLoginError) {
1568
- const error = new CredentialUnavailableError("Please run 'az login' from a command prompt to authenticate before using this credential.");
1569
- logger$i.getToken.info(formatError(scopes, error));
1570
- throw error;
1571
- }
1572
- try {
1573
- const responseData = obj.stdout;
1574
- const response = JSON.parse(responseData);
1575
- logger$i.getToken.info(formatSuccess(scopes));
1576
- const returnValue = {
1577
- token: response.accessToken,
1578
- expiresOnTimestamp: new Date(response.expiresOn).getTime(),
1579
- };
1580
- return returnValue;
1581
- }
1582
- catch (e) {
1583
- if (obj.stderr) {
1584
- throw new CredentialUnavailableError(obj.stderr);
1585
- }
1586
- throw e;
1587
- }
1588
- }
1589
- catch (err) {
1590
- const error = err.name === "CredentialUnavailableError"
1591
- ? err
1592
- : new CredentialUnavailableError(err.message || "Unknown error while trying to retrieve the access token");
1593
- logger$i.getToken.info(formatError(scopes, error));
1594
- throw error;
1595
- }
1596
- });
1527
+ const body = {
1528
+ resource,
1529
+ };
1530
+ if (clientId) {
1531
+ body.client_id = clientId;
1532
+ }
1533
+ if (resourceId) {
1534
+ body.msi_res_id = resourceId;
1535
+ }
1536
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
1537
+ if (!process.env.MSI_ENDPOINT) {
1538
+ throw new Error(`${msiName$5}: Missing environment variable: MSI_ENDPOINT`);
1597
1539
  }
1540
+ const params = new URLSearchParams(body);
1541
+ return {
1542
+ url: process.env.MSI_ENDPOINT,
1543
+ method: "POST",
1544
+ body: params.toString(),
1545
+ headers: coreRestPipeline.createHttpHeaders({
1546
+ Accept: "application/json",
1547
+ Metadata: "true",
1548
+ "Content-Type": "application/x-www-form-urlencoded",
1549
+ }),
1550
+ };
1598
1551
  }
1599
-
1600
- // Copyright (c) Microsoft Corporation.
1601
1552
  /**
1602
- * Easy to mock childProcess utils.
1603
- * @internal
1553
+ * Defines how to determine whether the Azure Cloud Shell MSI is available, and also how to retrieve a token from the Azure Cloud Shell MSI.
1554
+ * Since Azure Managed Identities aren't available in the Azure Cloud Shell, we log a warning for users that try to access cloud shell using user assigned identity.
1604
1555
  */
1605
- const processUtils = {
1606
- /**
1607
- * Promisifying childProcess.execFile
1608
- * @internal
1609
- */
1610
- execFile(file, params, options) {
1611
- return new Promise((resolve, reject) => {
1612
- child_process__namespace.execFile(file, params, options, (error, stdout, stderr) => {
1613
- if (Buffer.isBuffer(stdout)) {
1614
- stdout = stdout.toString("utf8");
1615
- }
1616
- if (Buffer.isBuffer(stderr)) {
1617
- stderr = stderr.toString("utf8");
1618
- }
1619
- if (stderr || error) {
1620
- reject(stderr ? new Error(stderr) : error);
1621
- }
1622
- else {
1623
- resolve(stdout);
1624
- }
1625
- });
1626
- });
1556
+ const cloudShellMsi = {
1557
+ async isAvailable({ scopes }) {
1558
+ const resource = mapScopesToResource(scopes);
1559
+ if (!resource) {
1560
+ logger$i.info(`${msiName$5}: Unavailable. Multiple scopes are not supported.`);
1561
+ return false;
1562
+ }
1563
+ const result = Boolean(process.env.MSI_ENDPOINT);
1564
+ if (!result) {
1565
+ logger$i.info(`${msiName$5}: Unavailable. The environment variable MSI_ENDPOINT is needed.`);
1566
+ }
1567
+ return result;
1568
+ },
1569
+ async getToken(configuration, getTokenOptions = {}) {
1570
+ const { identityClient, scopes, clientId, resourceId } = configuration;
1571
+ if (clientId) {
1572
+ logger$i.warning(`${msiName$5}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
1573
+ }
1574
+ if (resourceId) {
1575
+ logger$i.warning(`${msiName$5}: user defined managed Identity by resource Id not supported. The argument resourceId might be ignored by the service.`);
1576
+ }
1577
+ logger$i.info(`${msiName$5}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`);
1578
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$5(scopes, clientId, resourceId)), {
1579
+ // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
1580
+ allowInsecureConnection: true }));
1581
+ const tokenResponse = await identityClient.sendTokenRequest(request);
1582
+ return (tokenResponse && tokenResponse.accessToken) || null;
1627
1583
  },
1628
1584
  };
1629
1585
 
1630
1586
  // Copyright (c) Microsoft Corporation.
1631
- const logger$h = credentialLogger("AzurePowerShellCredential");
1632
- const isWindows = process.platform === "win32";
1587
+ const msiName$4 = "ManagedIdentityCredential - IMDS";
1588
+ const logger$h = credentialLogger(msiName$4);
1633
1589
  /**
1634
- * Returns a platform-appropriate command name by appending ".exe" on Windows.
1635
- *
1636
- * @internal
1590
+ * Generates the options used on the request for an access token.
1637
1591
  */
1638
- function formatCommand(commandName) {
1639
- if (isWindows) {
1640
- return `${commandName}.exe`;
1592
+ function prepareRequestOptions$4(scopes, clientId, resourceId, options) {
1593
+ var _a;
1594
+ const resource = mapScopesToResource(scopes);
1595
+ if (!resource) {
1596
+ throw new Error(`${msiName$4}: Multiple scopes are not supported.`);
1641
1597
  }
1642
- else {
1643
- return commandName;
1598
+ const { skipQuery, skipMetadataHeader } = options || {};
1599
+ let query = "";
1600
+ // Pod Identity will try to process this request even if the Metadata header is missing.
1601
+ // We can exclude the request query to ensure no IMDS endpoint tries to process the ping request.
1602
+ if (!skipQuery) {
1603
+ const queryParameters = {
1604
+ resource,
1605
+ "api-version": imdsApiVersion,
1606
+ };
1607
+ if (clientId) {
1608
+ queryParameters.client_id = clientId;
1609
+ }
1610
+ if (resourceId) {
1611
+ queryParameters.msi_res_id = resourceId;
1612
+ }
1613
+ const params = new URLSearchParams(queryParameters);
1614
+ query = `?${params.toString()}`;
1644
1615
  }
1645
- }
1646
- /**
1647
- * Receives a list of commands to run, executes them, then returns the outputs.
1648
- * If anything fails, an error is thrown.
1649
- * @internal
1650
- */
1651
- async function runCommands(commands) {
1652
- const results = [];
1653
- for (const command of commands) {
1654
- const [file, ...parameters] = command;
1655
- const result = (await processUtils.execFile(file, parameters, { encoding: "utf8" }));
1656
- results.push(result);
1616
+ const url = new URL(imdsEndpointPath, (_a = process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : imdsHost);
1617
+ const rawHeaders = {
1618
+ Accept: "application/json",
1619
+ Metadata: "true",
1620
+ };
1621
+ // Remove the Metadata header to invoke a request error from some IMDS endpoints.
1622
+ if (skipMetadataHeader) {
1623
+ delete rawHeaders.Metadata;
1657
1624
  }
1658
- return results;
1625
+ return {
1626
+ // In this case, the `?` should be added in the "query" variable `skipQuery` is not set.
1627
+ url: `${url}${query}`,
1628
+ method: "GET",
1629
+ headers: coreRestPipeline.createHttpHeaders(rawHeaders),
1630
+ };
1659
1631
  }
1632
+ // 800ms -> 1600ms -> 3200ms
1633
+ const imdsMsiRetryConfig = {
1634
+ maxRetries: 3,
1635
+ startDelayInMs: 800,
1636
+ intervalIncrement: 2,
1637
+ };
1660
1638
  /**
1661
- * Known PowerShell errors
1662
- * @internal
1639
+ * Defines how to determine whether the Azure IMDS MSI is available, and also how to retrieve a token from the Azure IMDS MSI.
1663
1640
  */
1664
- const powerShellErrors = {
1665
- login: "Run Connect-AzAccount to login",
1666
- installed: "The specified module 'Az.Accounts' with version '2.2.0' was not loaded because no valid module file was found in any module directory",
1641
+ const imdsMsi = {
1642
+ async isAvailable({ scopes, identityClient, clientId, resourceId, getTokenOptions = {}, }) {
1643
+ const resource = mapScopesToResource(scopes);
1644
+ if (!resource) {
1645
+ logger$h.info(`${msiName$4}: Unavailable. Multiple scopes are not supported.`);
1646
+ return false;
1647
+ }
1648
+ // if the PodIdentityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist
1649
+ if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
1650
+ return true;
1651
+ }
1652
+ if (!identityClient) {
1653
+ throw new Error("Missing IdentityClient");
1654
+ }
1655
+ const requestOptions = prepareRequestOptions$4(resource, clientId, resourceId, {
1656
+ skipMetadataHeader: true,
1657
+ skipQuery: true,
1658
+ });
1659
+ return tracingClient.withSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions, async (options) => {
1660
+ var _a;
1661
+ requestOptions.tracingOptions = options.tracingOptions;
1662
+ // Create a request with a timeout since we expect that
1663
+ // not having a "Metadata" header should cause an error to be
1664
+ // returned quickly from the endpoint, proving its availability.
1665
+ const request = coreRestPipeline.createPipelineRequest(requestOptions);
1666
+ // Default to 300 if the default of 0 is used.
1667
+ // Negative values can still be used to disable the timeout.
1668
+ request.timeout = ((_a = options.requestOptions) === null || _a === void 0 ? void 0 : _a.timeout) || 300;
1669
+ // This MSI uses the imdsEndpoint to get the token, which only uses http://
1670
+ request.allowInsecureConnection = true;
1671
+ try {
1672
+ logger$h.info(`${msiName$4}: Pinging the Azure IMDS endpoint`);
1673
+ await identityClient.sendRequest(request);
1674
+ }
1675
+ catch (err) {
1676
+ // If the request failed, or Node.js was unable to establish a connection,
1677
+ // or the host was down, we'll assume the IMDS endpoint isn't available.
1678
+ if (coreUtil.isError(err)) {
1679
+ logger$h.verbose(`${msiName$4}: Caught error ${err.name}: ${err.message}`);
1680
+ }
1681
+ logger$h.info(`${msiName$4}: The Azure IMDS endpoint is unavailable`);
1682
+ return false;
1683
+ }
1684
+ // If we received any response, the endpoint is available
1685
+ logger$h.info(`${msiName$4}: The Azure IMDS endpoint is available`);
1686
+ return true;
1687
+ });
1688
+ },
1689
+ async getToken(configuration, getTokenOptions = {}) {
1690
+ const { identityClient, scopes, clientId, resourceId } = configuration;
1691
+ if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
1692
+ logger$h.info(`${msiName$4}: Using the Azure IMDS endpoint coming from the environment variable AZURE_POD_IDENTITY_AUTHORITY_HOST=${process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST}.`);
1693
+ }
1694
+ else {
1695
+ logger$h.info(`${msiName$4}: Using the default Azure IMDS endpoint ${imdsHost}.`);
1696
+ }
1697
+ let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;
1698
+ for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {
1699
+ try {
1700
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$4(scopes, clientId, resourceId)), { allowInsecureConnection: true }));
1701
+ const tokenResponse = await identityClient.sendTokenRequest(request);
1702
+ return (tokenResponse && tokenResponse.accessToken) || null;
1703
+ }
1704
+ catch (error) {
1705
+ if (error.statusCode === 404) {
1706
+ await coreUtil.delay(nextDelayInMs);
1707
+ nextDelayInMs *= imdsMsiRetryConfig.intervalIncrement;
1708
+ continue;
1709
+ }
1710
+ throw error;
1711
+ }
1712
+ }
1713
+ throw new AuthenticationError(404, `${msiName$4}: Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`);
1714
+ },
1667
1715
  };
1716
+
1717
+ // Copyright (c) Microsoft Corporation.
1718
+ const msiName$3 = "ManagedIdentityCredential - Azure Arc MSI";
1719
+ const logger$g = credentialLogger(msiName$3);
1668
1720
  /**
1669
- * Messages to use when throwing in this credential.
1670
- * @internal
1721
+ * Generates the options used on the request for an access token.
1671
1722
  */
1672
- const powerShellPublicErrorMessages = {
1673
- login: "Please run 'Connect-AzAccount' from PowerShell to authenticate before using this credential.",
1674
- installed: `The 'Az.Account' module >= 2.2.0 is not installed. Install the Azure Az PowerShell module with: "Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force".`,
1675
- troubleshoot: `To troubleshoot, visit https://aka.ms/azsdk/js/identity/powershellcredential/troubleshoot.`,
1676
- };
1677
- // PowerShell Azure User not logged in error check.
1678
- const isLoginError = (err) => err.message.match(`(.*)${powerShellErrors.login}(.*)`);
1679
- // Az Module not Installed in Azure PowerShell check.
1680
- const isNotInstalledError = (err) => err.message.match(powerShellErrors.installed);
1681
- /**
1682
- * The PowerShell commands to be tried, in order.
1683
- *
1684
- * @internal
1685
- */
1686
- const commandStack = [formatCommand("pwsh")];
1687
- if (isWindows) {
1688
- commandStack.push(formatCommand("powershell"));
1689
- }
1690
- /**
1691
- * This credential will use the currently logged-in user information from the
1692
- * Azure PowerShell module. To do so, it will read the user access token and
1693
- * expire time with Azure PowerShell command `Get-AzAccessToken -ResourceUrl {ResourceScope}`
1694
- */
1695
- class AzurePowerShellCredential {
1696
- /**
1697
- * Creates an instance of the {@link AzurePowerShellCredential}.
1698
- *
1699
- * To use this credential:
1700
- * - Install the Azure Az PowerShell module with:
1701
- * `Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force`.
1702
- * - You have already logged in to Azure PowerShell using the command
1703
- * `Connect-AzAccount` from the command line.
1704
- *
1705
- * @param options - Options, to optionally allow multi-tenant requests.
1706
- */
1707
- constructor(options) {
1708
- this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
1709
- }
1710
- /**
1711
- * Gets the access token from Azure PowerShell
1712
- * @param resource - The resource to use when getting the token
1713
- */
1714
- async getAzurePowerShellAccessToken(resource, tenantId) {
1715
- // Clone the stack to avoid mutating it while iterating
1716
- for (const powerShellCommand of [...commandStack]) {
1717
- try {
1718
- await runCommands([[powerShellCommand, "/?"]]);
1719
- }
1720
- catch (e) {
1721
- // Remove this credential from the original stack so that we don't try it again.
1722
- commandStack.shift();
1723
- continue;
1724
- }
1725
- let tenantSection = "";
1726
- if (tenantId) {
1727
- tenantSection = `-TenantId "${tenantId}"`;
1728
- }
1729
- const results = await runCommands([
1730
- [
1731
- powerShellCommand,
1732
- "-Command",
1733
- "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru",
1734
- ],
1735
- [
1736
- powerShellCommand,
1737
- "-Command",
1738
- `Get-AzAccessToken ${tenantSection} -ResourceUrl "${resource}" | ConvertTo-Json`,
1739
- ],
1740
- ]);
1741
- const result = results[1];
1742
- try {
1743
- return JSON.parse(result);
1744
- }
1745
- catch (e) {
1746
- throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
1747
- }
1748
- }
1749
- throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`);
1750
- }
1751
- /**
1752
- * Authenticates with Azure Active Directory and returns an access token if successful.
1753
- * If the authentication cannot be performed through PowerShell, a {@link CredentialUnavailableError} will be thrown.
1754
- *
1755
- * @param scopes - The list of scopes for which the token will have access.
1756
- * @param options - The options used to configure any requests this TokenCredential implementation might make.
1757
- */
1758
- async getToken(scopes, options = {}) {
1759
- return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => {
1760
- const tenantId = processMultiTenantRequest(this.tenantId, options);
1761
- if (tenantId) {
1762
- checkTenantId(logger$h, tenantId);
1763
- }
1764
- const scope = typeof scopes === "string" ? scopes : scopes[0];
1765
- ensureValidScope(scope, logger$h);
1766
- logger$h.getToken.info(`Using the scope ${scope}`);
1767
- const resource = getScopeResource(scope);
1768
- try {
1769
- const response = await this.getAzurePowerShellAccessToken(resource, tenantId);
1770
- logger$h.getToken.info(formatSuccess(scopes));
1771
- return {
1772
- token: response.Token,
1773
- expiresOnTimestamp: new Date(response.ExpiresOn).getTime(),
1774
- };
1775
- }
1776
- catch (err) {
1777
- if (isNotInstalledError(err)) {
1778
- const error = new CredentialUnavailableError(powerShellPublicErrorMessages.installed);
1779
- logger$h.getToken.info(formatError(scope, error));
1780
- throw error;
1781
- }
1782
- else if (isLoginError(err)) {
1783
- const error = new CredentialUnavailableError(powerShellPublicErrorMessages.login);
1784
- logger$h.getToken.info(formatError(scope, error));
1785
- throw error;
1786
- }
1787
- const error = new CredentialUnavailableError(`${err}. ${powerShellPublicErrorMessages.troubleshoot}`);
1788
- logger$h.getToken.info(formatError(scope, error));
1789
- throw error;
1790
- }
1791
- });
1792
- }
1793
- }
1794
-
1795
- // Copyright (c) Microsoft Corporation.
1796
- /**
1797
- * MSAL client secret client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
1798
- * @internal
1799
- */
1800
- class MsalClientSecret extends MsalNode {
1801
- constructor(options) {
1802
- super(options);
1803
- this.requiresConfidential = true;
1804
- this.msalConfig.auth.clientSecret = options.clientSecret;
1805
- }
1806
- async doGetToken(scopes, options = {}) {
1807
- try {
1808
- const result = await this.confidentialApp.acquireTokenByClientCredential({
1809
- scopes,
1810
- correlationId: options.correlationId,
1811
- azureRegion: this.azureRegion,
1812
- authority: options.authority,
1813
- claims: options.claims,
1814
- });
1815
- // The Client Credential flow does not return an account,
1816
- // so each time getToken gets called, we will have to acquire a new token through the service.
1817
- return this.handleResult(scopes, this.clientId, result || undefined);
1818
- }
1819
- catch (err) {
1820
- throw this.handleError(scopes, err, options);
1821
- }
1822
- }
1823
- }
1824
-
1825
- // Copyright (c) Microsoft Corporation.
1826
- const logger$g = credentialLogger("ClientSecretCredential");
1827
- /**
1828
- * Enables authentication to Azure Active Directory using a client secret
1829
- * that was generated for an App Registration. More information on how
1830
- * to configure a client secret can be found here:
1831
- *
1832
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application
1833
- *
1834
- */
1835
- class ClientSecretCredential {
1836
- /**
1837
- * Creates an instance of the ClientSecretCredential with the details
1838
- * needed to authenticate against Azure Active Directory with a client
1839
- * secret.
1840
- *
1841
- * @param tenantId - The Azure Active Directory tenant (directory) ID.
1842
- * @param clientId - The client (application) ID of an App Registration in the tenant.
1843
- * @param clientSecret - A client secret that was generated for the App Registration.
1844
- * @param options - Options for configuring the client which makes the authentication request.
1845
- */
1846
- constructor(tenantId, clientId, clientSecret, options = {}) {
1847
- if (!tenantId || !clientId || !clientSecret) {
1848
- throw new Error("ClientSecretCredential: tenantId, clientId, and clientSecret are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
1849
- }
1850
- this.msalFlow = new MsalClientSecret(Object.assign(Object.assign({}, options), { logger: logger$g,
1851
- clientId,
1852
- tenantId,
1853
- clientSecret, tokenCredentialOptions: options }));
1854
- }
1855
- /**
1856
- * Authenticates with Azure Active Directory and returns an access token if successful.
1857
- * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
1858
- *
1859
- * @param scopes - The list of scopes for which the token will have access.
1860
- * @param options - The options used to configure any requests this
1861
- * TokenCredential implementation might make.
1862
- */
1863
- async getToken(scopes, options = {}) {
1864
- return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
1865
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1866
- return this.msalFlow.getToken(arrayScopes, newOptions);
1867
- });
1868
- }
1869
- }
1870
-
1871
- // Copyright (c) Microsoft Corporation.
1872
- const readFileAsync$2 = util.promisify(fs.readFile);
1873
- /**
1874
- * Tries to asynchronously load a certificate from the given path.
1875
- *
1876
- * @param configuration - Either the PEM value or the path to the certificate.
1877
- * @param sendCertificateChain - Option to include x5c header for SubjectName and Issuer name authorization.
1878
- * @returns - The certificate parts, or `undefined` if the certificate could not be loaded.
1879
- * @internal
1880
- */
1881
- async function parseCertificate(configuration, sendCertificateChain) {
1882
- const certificateParts = {};
1883
- const certificate = configuration
1884
- .certificate;
1885
- const certificatePath = configuration
1886
- .certificatePath;
1887
- certificateParts.certificateContents =
1888
- certificate || (await readFileAsync$2(certificatePath, "utf8"));
1889
- if (sendCertificateChain) {
1890
- certificateParts.x5c = certificateParts.certificateContents;
1891
- }
1892
- const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
1893
- const publicKeys = [];
1894
- // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
1895
- let match;
1896
- do {
1897
- match = certificatePattern.exec(certificateParts.certificateContents);
1898
- if (match) {
1899
- publicKeys.push(match[3]);
1900
- }
1901
- } while (match);
1902
- if (publicKeys.length === 0) {
1903
- throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
1904
- }
1905
- certificateParts.thumbprint = crypto.createHash("sha1")
1906
- .update(Buffer.from(publicKeys[0], "base64"))
1907
- .digest("hex")
1908
- .toUpperCase();
1909
- return certificateParts;
1910
- }
1911
- /**
1912
- * MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
1913
- * @internal
1914
- */
1915
- class MsalClientCertificate extends MsalNode {
1916
- constructor(options) {
1917
- super(options);
1918
- this.requiresConfidential = true;
1919
- this.configuration = options.configuration;
1920
- this.sendCertificateChain = options.sendCertificateChain;
1921
- }
1922
- // Changing the MSAL configuration asynchronously
1923
- async init(options) {
1924
- try {
1925
- const parts = await parseCertificate(this.configuration, this.sendCertificateChain);
1926
- let privateKey;
1927
- if (this.configuration.certificatePassword !== undefined) {
1928
- const privateKeyObject = crypto.createPrivateKey({
1929
- key: parts.certificateContents,
1930
- passphrase: this.configuration.certificatePassword,
1931
- format: "pem",
1932
- });
1933
- privateKey = privateKeyObject
1934
- .export({
1935
- format: "pem",
1936
- type: "pkcs8",
1937
- })
1938
- .toString();
1939
- }
1940
- else {
1941
- privateKey = parts.certificateContents;
1942
- }
1943
- this.msalConfig.auth.clientCertificate = {
1944
- thumbprint: parts.thumbprint,
1945
- privateKey: privateKey,
1946
- x5c: parts.x5c,
1947
- };
1948
- }
1949
- catch (error) {
1950
- this.logger.info(formatError("", error));
1951
- throw error;
1952
- }
1953
- return super.init(options);
1723
+ function prepareRequestOptions$3(scopes, clientId, resourceId) {
1724
+ const resource = mapScopesToResource(scopes);
1725
+ if (!resource) {
1726
+ throw new Error(`${msiName$3}: Multiple scopes are not supported.`);
1954
1727
  }
1955
- async doGetToken(scopes, options = {}) {
1956
- try {
1957
- const clientCredReq = {
1958
- scopes,
1959
- correlationId: options.correlationId,
1960
- azureRegion: this.azureRegion,
1961
- authority: options.authority,
1962
- claims: options.claims,
1963
- };
1964
- const result = await this.confidentialApp.acquireTokenByClientCredential(clientCredReq);
1965
- // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential,
1966
- // The Client Credential flow does not return the account information from the authentication service,
1967
- // so each time getToken gets called, we will have to acquire a new token through the service.
1968
- return this.handleResult(scopes, this.clientId, result || undefined);
1969
- }
1970
- catch (err) {
1971
- throw this.handleError(scopes, err, options);
1972
- }
1728
+ const queryParameters = {
1729
+ resource,
1730
+ "api-version": azureArcAPIVersion,
1731
+ };
1732
+ if (clientId) {
1733
+ queryParameters.client_id = clientId;
1973
1734
  }
1974
- }
1975
-
1976
- // Copyright (c) Microsoft Corporation.
1977
- const credentialName$2 = "ClientCertificateCredential";
1978
- const logger$f = credentialLogger(credentialName$2);
1979
- /**
1980
- * Enables authentication to Azure Active Directory using a PEM-encoded
1981
- * certificate that is assigned to an App Registration. More information
1982
- * on how to configure certificate authentication can be found here:
1983
- *
1984
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad
1985
- *
1986
- */
1987
- class ClientCertificateCredential {
1988
- constructor(tenantId, clientId, certificatePathOrConfiguration, options = {}) {
1989
- if (!tenantId || !clientId) {
1990
- throw new Error(`${credentialName$2}: tenantId and clientId are required parameters.`);
1991
- }
1992
- const configuration = Object.assign({}, (typeof certificatePathOrConfiguration === "string"
1993
- ? {
1994
- certificatePath: certificatePathOrConfiguration,
1995
- }
1996
- : certificatePathOrConfiguration));
1997
- const certificate = configuration
1998
- .certificate;
1999
- const certificatePath = configuration.certificatePath;
2000
- if (!configuration || !(certificate || certificatePath)) {
2001
- throw new Error(`${credentialName$2}: Provide either a PEM certificate in string form, or the path to that certificate in the filesystem. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
2002
- }
2003
- if (certificate && certificatePath) {
2004
- throw new Error(`${credentialName$2}: To avoid unexpected behaviors, providing both the contents of a PEM certificate and the path to a PEM certificate is forbidden. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
2005
- }
2006
- this.msalFlow = new MsalClientCertificate(Object.assign(Object.assign({}, options), { configuration,
2007
- logger: logger$f,
2008
- clientId,
2009
- tenantId, sendCertificateChain: options.sendCertificateChain, tokenCredentialOptions: options }));
1735
+ if (resourceId) {
1736
+ queryParameters.msi_res_id = resourceId;
2010
1737
  }
2011
- /**
2012
- * Authenticates with Azure Active Directory and returns an access token if successful.
2013
- * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2014
- *
2015
- * @param scopes - The list of scopes for which the token will have access.
2016
- * @param options - The options used to configure any requests this
2017
- * TokenCredential implementation might make.
2018
- */
2019
- async getToken(scopes, options = {}) {
2020
- return tracingClient.withSpan(`${credentialName$2}.getToken`, options, async (newOptions) => {
2021
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2022
- return this.msalFlow.getToken(arrayScopes, newOptions);
2023
- });
1738
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
1739
+ if (!process.env.IDENTITY_ENDPOINT) {
1740
+ throw new Error(`${msiName$3}: Missing environment variable: IDENTITY_ENDPOINT`);
2024
1741
  }
1742
+ const query = new URLSearchParams(queryParameters);
1743
+ return coreRestPipeline.createPipelineRequest({
1744
+ // Should be similar to: http://localhost:40342/metadata/identity/oauth2/token
1745
+ url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
1746
+ method: "GET",
1747
+ headers: coreRestPipeline.createHttpHeaders({
1748
+ Accept: "application/json",
1749
+ Metadata: "true",
1750
+ }),
1751
+ });
2025
1752
  }
2026
-
2027
- // Copyright (c) Microsoft Corporation.
2028
1753
  /**
2029
- * MSAL username and password client. Calls to the MSAL's public application's `acquireTokenByUsernamePassword` during `doGetToken`.
2030
- * @internal
2031
- */
2032
- class MsalUsernamePassword extends MsalNode {
2033
- constructor(options) {
2034
- super(options);
2035
- this.username = options.username;
2036
- this.password = options.password;
2037
- }
2038
- async doGetToken(scopes, options) {
2039
- try {
2040
- const requestOptions = {
2041
- scopes,
2042
- username: this.username,
2043
- password: this.password,
2044
- correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
2045
- authority: options === null || options === void 0 ? void 0 : options.authority,
2046
- claims: options === null || options === void 0 ? void 0 : options.claims,
2047
- };
2048
- const result = await this.publicApp.acquireTokenByUsernamePassword(requestOptions);
2049
- return this.handleResult(scopes, this.clientId, result || undefined);
2050
- }
2051
- catch (error) {
2052
- throw this.handleError(scopes, error, options);
1754
+ * Retrieves the file contents at the given path using promises.
1755
+ * Useful since `fs`'s readFileSync locks the thread, and to avoid extra dependencies.
1756
+ */
1757
+ function readFileAsync$2(path, options) {
1758
+ return new Promise((resolve, reject) => fs.readFile(path, options, (err, data) => {
1759
+ if (err) {
1760
+ reject(err);
2053
1761
  }
2054
- }
1762
+ resolve(data);
1763
+ }));
2055
1764
  }
2056
-
2057
- // Copyright (c) Microsoft Corporation.
2058
- const logger$e = credentialLogger("UsernamePasswordCredential");
2059
1765
  /**
2060
- * Enables authentication to Azure Active Directory with a user's
2061
- * username and password. This credential requires a high degree of
2062
- * trust so you should only use it when other, more secure credential
2063
- * types can't be used.
1766
+ * Does a request to the authentication provider that results in a file path.
2064
1767
  */
2065
- class UsernamePasswordCredential {
2066
- /**
2067
- * Creates an instance of the UsernamePasswordCredential with the details
2068
- * needed to authenticate against Azure Active Directory with a username
2069
- * and password.
2070
- *
2071
- * @param tenantId - The Azure Active Directory tenant (directory).
2072
- * @param clientId - The client (application) ID of an App Registration in the tenant.
2073
- * @param username - The user account's e-mail address (user name).
2074
- * @param password - The user account's account password
2075
- * @param options - Options for configuring the client which makes the authentication request.
2076
- */
2077
- constructor(tenantId, clientId, username, password, options = {}) {
2078
- if (!tenantId || !clientId || !username || !password) {
2079
- throw new Error("UsernamePasswordCredential: tenantId, clientId, username and password are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
1768
+ async function filePathRequest(identityClient, requestPrepareOptions) {
1769
+ const response = await identityClient.sendRequest(coreRestPipeline.createPipelineRequest(requestPrepareOptions));
1770
+ if (response.status !== 401) {
1771
+ let message = "";
1772
+ if (response.bodyAsText) {
1773
+ message = ` Response: ${response.bodyAsText}`;
2080
1774
  }
2081
- this.msalFlow = new MsalUsernamePassword(Object.assign(Object.assign({}, options), { logger: logger$e,
2082
- clientId,
2083
- tenantId,
2084
- username,
2085
- password, tokenCredentialOptions: options || {} }));
1775
+ throw new AuthenticationError(response.status, `${msiName$3}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`);
2086
1776
  }
2087
- /**
2088
- * Authenticates with Azure Active Directory and returns an access token if successful.
2089
- * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2090
- *
2091
- * If the user provided the option `disableAutomaticAuthentication`,
2092
- * once the token can't be retrieved silently,
2093
- * this method won't attempt to request user interaction to retrieve the token.
2094
- *
2095
- * @param scopes - The list of scopes for which the token will have access.
2096
- * @param options - The options used to configure any requests this
2097
- * TokenCredential implementation might make.
2098
- */
2099
- async getToken(scopes, options = {}) {
2100
- return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
2101
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2102
- return this.msalFlow.getToken(arrayScopes, newOptions);
2103
- });
1777
+ const authHeader = response.headers.get("www-authenticate") || "";
1778
+ try {
1779
+ return authHeader.split("=").slice(1)[0];
1780
+ }
1781
+ catch (e) {
1782
+ throw Error(`Invalid www-authenticate header format: ${authHeader}`);
2104
1783
  }
2105
1784
  }
2106
-
2107
- // Copyright (c) Microsoft Corporation.
2108
- /**
2109
- * Contains the list of all supported environment variable names so that an
2110
- * appropriate error message can be generated when no credentials can be
2111
- * configured.
2112
- *
2113
- * @internal
2114
- */
2115
- const AllSupportedEnvironmentVariables = [
2116
- "AZURE_TENANT_ID",
2117
- "AZURE_CLIENT_ID",
2118
- "AZURE_CLIENT_SECRET",
2119
- "AZURE_CLIENT_CERTIFICATE_PATH",
2120
- "AZURE_CLIENT_CERTIFICATE_PASSWORD",
2121
- "AZURE_USERNAME",
2122
- "AZURE_PASSWORD",
2123
- ];
2124
- const credentialName$1 = "EnvironmentCredential";
2125
- const logger$d = credentialLogger(credentialName$1);
2126
1785
  /**
2127
- * Enables authentication to Azure Active Directory using client secret
2128
- * details configured in environment variables
1786
+ * Defines how to determine whether the Azure Arc MSI is available, and also how to retrieve a token from the Azure Arc MSI.
2129
1787
  */
2130
- class EnvironmentCredential {
2131
- /**
2132
- * Creates an instance of the EnvironmentCredential class and decides what credential to use depending on the available environment variables.
2133
- *
2134
- * Required environment variables:
2135
- * - `AZURE_TENANT_ID`: The Azure Active Directory tenant (directory) ID.
2136
- * - `AZURE_CLIENT_ID`: The client (application) ID of an App Registration in the tenant.
2137
- *
2138
- * Environment variables used for client credential authentication:
2139
- * - `AZURE_CLIENT_SECRET`: A client secret that was generated for the App Registration.
2140
- * - `AZURE_CLIENT_CERTIFICATE_PATH`: The path to a PEM certificate to use during the authentication, instead of the client secret.
2141
- * - `AZURE_CLIENT_CERTIFICATE_PASSWORD`: (optional) password for the certificate file.
2142
- *
2143
- * Alternatively, users can provide environment variables for username and password authentication:
2144
- * - `AZURE_USERNAME`: Username to authenticate with.
2145
- * - `AZURE_PASSWORD`: Password to authenticate with.
2146
- *
2147
- * If the environment variables required to perform the authentication are missing, a {@link CredentialUnavailableError} will be thrown.
2148
- * If the authentication fails, or if there's an unknown error, an {@link AuthenticationError} will be thrown.
2149
- *
2150
- * @param options - Options for configuring the client which makes the authentication request.
2151
- */
2152
- constructor(options) {
2153
- // Keep track of any missing environment variables for error details
2154
- this._credential = undefined;
2155
- const assigned = processEnvVars(AllSupportedEnvironmentVariables).assigned.join(", ");
2156
- logger$d.info(`Found the following environment variables: ${assigned}`);
2157
- const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
2158
- if (tenantId) {
2159
- checkTenantId(logger$d, tenantId);
1788
+ const arcMsi = {
1789
+ async isAvailable({ scopes }) {
1790
+ const resource = mapScopesToResource(scopes);
1791
+ if (!resource) {
1792
+ logger$g.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`);
1793
+ return false;
2160
1794
  }
2161
- if (tenantId && clientId && clientSecret) {
2162
- logger$d.info(`Invoking ClientSecretCredential with tenant ID: ${tenantId}, clientId: ${clientId} and clientSecret: [REDACTED]`);
2163
- this._credential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
2164
- return;
1795
+ const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
1796
+ if (!result) {
1797
+ logger$g.info(`${msiName$3}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`);
2165
1798
  }
2166
- const certificatePath = process.env.AZURE_CLIENT_CERTIFICATE_PATH;
2167
- const certificatePassword = process.env.AZURE_CLIENT_CERTIFICATE_PASSWORD;
2168
- if (tenantId && clientId && certificatePath) {
2169
- logger$d.info(`Invoking ClientCertificateCredential with tenant ID: ${tenantId}, clientId: ${clientId} and certificatePath: ${certificatePath}`);
2170
- this._credential = new ClientCertificateCredential(tenantId, clientId, { certificatePath, certificatePassword }, options);
2171
- return;
1799
+ return result;
1800
+ },
1801
+ async getToken(configuration, getTokenOptions = {}) {
1802
+ var _a;
1803
+ const { identityClient, scopes, clientId, resourceId } = configuration;
1804
+ if (clientId) {
1805
+ logger$g.warning(`${msiName$3}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
2172
1806
  }
2173
- const username = process.env.AZURE_USERNAME;
2174
- const password = process.env.AZURE_PASSWORD;
2175
- if (tenantId && clientId && username && password) {
2176
- logger$d.info(`Invoking UsernamePasswordCredential with tenant ID: ${tenantId}, clientId: ${clientId} and username: ${username}`);
2177
- this._credential = new UsernamePasswordCredential(tenantId, clientId, username, password, options);
1807
+ if (resourceId) {
1808
+ logger$g.warning(`${msiName$3}: user defined managed Identity by resource Id is not supported. Argument resourceId will be ignored.`);
2178
1809
  }
2179
- }
2180
- /**
2181
- * Authenticates with Azure Active Directory and returns an access token if successful.
2182
- *
2183
- * @param scopes - The list of scopes for which the token will have access.
2184
- * @param options - Optional parameters. See {@link GetTokenOptions}.
2185
- */
2186
- async getToken(scopes, options = {}) {
2187
- return tracingClient.withSpan(`${credentialName$1}.getToken`, options, async (newOptions) => {
2188
- if (this._credential) {
2189
- try {
2190
- const result = await this._credential.getToken(scopes, newOptions);
2191
- logger$d.getToken.info(formatSuccess(scopes));
2192
- return result;
2193
- }
2194
- catch (err) {
2195
- const authenticationError = new AuthenticationError(400, {
2196
- error: `${credentialName$1} authentication failed. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.`,
2197
- error_description: err.message.toString().split("More details:").join(""),
2198
- });
2199
- logger$d.getToken.info(formatError(scopes, authenticationError));
2200
- throw authenticationError;
2201
- }
2202
- }
2203
- throw new CredentialUnavailableError(`${credentialName$1} is unavailable. No underlying credential could be used. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.`);
2204
- });
2205
- }
2206
- }
2207
-
2208
- // Copyright (c) Microsoft Corporation.
2209
- // Licensed under the MIT license.
2210
- const DefaultScopeSuffix = "/.default";
2211
- const imdsHost = "http://169.254.169.254";
2212
- const imdsEndpointPath = "/metadata/identity/oauth2/token";
2213
- const imdsApiVersion = "2018-02-01";
2214
- const azureArcAPIVersion = "2019-11-01";
2215
- const azureFabricVersion = "2019-07-01-preview";
1810
+ logger$g.info(`${msiName$3}: Authenticating.`);
1811
+ const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes, clientId, resourceId)), { allowInsecureConnection: true });
1812
+ const filePath = await filePathRequest(identityClient, requestOptions);
1813
+ if (!filePath) {
1814
+ throw new Error(`${msiName$3}: Failed to find the token file.`);
1815
+ }
1816
+ const key = await readFileAsync$2(filePath, { encoding: "utf-8" });
1817
+ (_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`);
1818
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({}, requestOptions), {
1819
+ // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
1820
+ allowInsecureConnection: true }));
1821
+ const tokenResponse = await identityClient.sendTokenRequest(request);
1822
+ return (tokenResponse && tokenResponse.accessToken) || null;
1823
+ },
1824
+ };
2216
1825
 
2217
1826
  // Copyright (c) Microsoft Corporation.
1827
+ const msiName$2 = "ManagedIdentityCredential - Token Exchange";
1828
+ const logger$f = credentialLogger(msiName$2);
1829
+ const readFileAsync$1 = util.promisify(fs__default["default"].readFile);
2218
1830
  /**
2219
- * Most MSIs send requests to the IMDS endpoint, or a similar endpoint.
2220
- * These are GET requests that require sending a `resource` parameter on the query.
2221
- * This resource can be derived from the scopes received through the getToken call, as long as only one scope is received.
2222
- * Multiple scopes assume that the resulting token will have access to multiple resources, which won't be the case.
2223
- *
2224
- * For that reason, when we encounter multiple scopes, we return undefined.
2225
- * It's up to the individual MSI implementations to throw the errors (which helps us provide less generic errors).
1831
+ * Generates the options used on the request for an access token.
2226
1832
  */
2227
- function mapScopesToResource(scopes) {
2228
- let scope = "";
2229
- if (Array.isArray(scopes)) {
2230
- if (scopes.length !== 1) {
2231
- return;
1833
+ function prepareRequestOptions$2(scopes, clientAssertion, clientId) {
1834
+ var _a;
1835
+ const bodyParams = {
1836
+ scope: Array.isArray(scopes) ? scopes.join(" ") : scopes,
1837
+ client_assertion: clientAssertion,
1838
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
1839
+ client_id: clientId,
1840
+ grant_type: "client_credentials",
1841
+ };
1842
+ const urlParams = new URLSearchParams(bodyParams);
1843
+ const url = new URL(`${process.env.AZURE_TENANT_ID}/oauth2/v2.0/token`, (_a = process.env.AZURE_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : DefaultAuthorityHost);
1844
+ return {
1845
+ url: url.toString(),
1846
+ method: "POST",
1847
+ body: urlParams.toString(),
1848
+ headers: coreRestPipeline.createHttpHeaders({
1849
+ Accept: "application/json",
1850
+ }),
1851
+ };
1852
+ }
1853
+ /**
1854
+ * Defines how to determine whether the token exchange MSI is available, and also how to retrieve a token from the token exchange MSI.
1855
+ */
1856
+ function tokenExchangeMsi() {
1857
+ const azureFederatedTokenFilePath = process.env.AZURE_FEDERATED_TOKEN_FILE;
1858
+ let azureFederatedTokenFileContent = undefined;
1859
+ let cacheDate = undefined;
1860
+ // Only reads from the assertion file once every 5 minutes
1861
+ async function readAssertion() {
1862
+ // Cached assertions expire after 5 minutes
1863
+ if (cacheDate !== undefined && Date.now() - cacheDate >= 1000 * 60 * 5) {
1864
+ azureFederatedTokenFileContent = undefined;
2232
1865
  }
2233
- scope = scopes[0];
2234
- }
2235
- else if (typeof scopes === "string") {
2236
- scope = scopes;
2237
- }
2238
- if (!scope.endsWith(DefaultScopeSuffix)) {
2239
- return scope;
1866
+ if (!azureFederatedTokenFileContent) {
1867
+ const file = await readFileAsync$1(azureFederatedTokenFilePath, "utf8");
1868
+ const value = file.trim();
1869
+ if (!value) {
1870
+ throw new Error(`No content on the file ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
1871
+ }
1872
+ else {
1873
+ azureFederatedTokenFileContent = value;
1874
+ cacheDate = Date.now();
1875
+ }
1876
+ }
1877
+ return azureFederatedTokenFileContent;
2240
1878
  }
2241
- return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix));
1879
+ return {
1880
+ async isAvailable({ clientId }) {
1881
+ const env = process.env;
1882
+ const result = Boolean((clientId || env.AZURE_CLIENT_ID) && env.AZURE_TENANT_ID && azureFederatedTokenFilePath);
1883
+ if (!result) {
1884
+ logger$f.info(`${msiName$2}: Unavailable. The environment variables needed are: AZURE_CLIENT_ID (or the client ID sent through the parameters), AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE`);
1885
+ }
1886
+ return result;
1887
+ },
1888
+ async getToken(configuration, getTokenOptions = {}) {
1889
+ const { identityClient, scopes, clientId } = configuration;
1890
+ logger$f.info(`${msiName$2}: Using the client assertion coming from environment variables.`);
1891
+ let assertion;
1892
+ try {
1893
+ assertion = await readAssertion();
1894
+ }
1895
+ catch (err) {
1896
+ throw new Error(`${msiName$2}: Failed to read ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
1897
+ }
1898
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$2(scopes, assertion, clientId || process.env.AZURE_CLIENT_ID)), {
1899
+ // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
1900
+ allowInsecureConnection: true }));
1901
+ const tokenResponse = await identityClient.sendTokenRequest(request);
1902
+ return (tokenResponse && tokenResponse.accessToken) || null;
1903
+ },
1904
+ };
2242
1905
  }
2243
1906
 
2244
1907
  // Copyright (c) Microsoft Corporation.
2245
- const msiName$6 = "ManagedIdentityCredential - AppServiceMSI 2017";
2246
- const logger$c = credentialLogger(msiName$6);
2247
- /**
2248
- * Formats the expiration date of the received token into the number of milliseconds between that date and midnight, January 1, 1970.
2249
- */
2250
- function expiresOnParser$3(requestBody) {
2251
- // App Service always returns string expires_on values.
2252
- return Date.parse(requestBody.expires_on);
2253
- }
1908
+ // This MSI can be easily tested by deploying a container to Azure Service Fabric with the Dockerfile:
1909
+ //
1910
+ // FROM node:12
1911
+ // RUN wget https://host.any/path/bash.sh
1912
+ // CMD ["bash", "bash.sh"]
1913
+ //
1914
+ // Where the bash script contains:
1915
+ //
1916
+ // curl --insecure $IDENTITY_ENDPOINT'?api-version=2019-07-01-preview&resource=https://vault.azure.net/' -H "Secret: $IDENTITY_HEADER"
1917
+ //
1918
+ const msiName$1 = "ManagedIdentityCredential - Fabric MSI";
1919
+ const logger$e = credentialLogger(msiName$1);
2254
1920
  /**
2255
1921
  * Generates the options used on the request for an access token.
2256
1922
  */
2257
- function prepareRequestOptions$6(scopes, clientId) {
1923
+ function prepareRequestOptions$1(scopes, clientId, resourceId) {
2258
1924
  const resource = mapScopesToResource(scopes);
2259
1925
  if (!resource) {
2260
- throw new Error(`${msiName$6}: Multiple scopes are not supported.`);
1926
+ throw new Error(`${msiName$1}: Multiple scopes are not supported.`);
2261
1927
  }
2262
1928
  const queryParameters = {
2263
1929
  resource,
2264
- "api-version": "2017-09-01",
1930
+ "api-version": azureFabricVersion,
2265
1931
  };
2266
1932
  if (clientId) {
2267
- queryParameters.clientid = clientId;
1933
+ queryParameters.client_id = clientId;
1934
+ }
1935
+ if (resourceId) {
1936
+ queryParameters.msi_res_id = resourceId;
2268
1937
  }
2269
1938
  const query = new URLSearchParams(queryParameters);
2270
1939
  // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2271
- if (!process.env.MSI_ENDPOINT) {
2272
- throw new Error(`${msiName$6}: Missing environment variable: MSI_ENDPOINT`);
1940
+ if (!process.env.IDENTITY_ENDPOINT) {
1941
+ throw new Error("Missing environment variable: IDENTITY_ENDPOINT");
2273
1942
  }
2274
- if (!process.env.MSI_SECRET) {
2275
- throw new Error(`${msiName$6}: Missing environment variable: MSI_SECRET`);
1943
+ if (!process.env.IDENTITY_HEADER) {
1944
+ throw new Error("Missing environment variable: IDENTITY_HEADER");
2276
1945
  }
2277
1946
  return {
2278
- url: `${process.env.MSI_ENDPOINT}?${query.toString()}`,
1947
+ url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
2279
1948
  method: "GET",
2280
1949
  headers: coreRestPipeline.createHttpHeaders({
2281
1950
  Accept: "application/json",
2282
- secret: process.env.MSI_SECRET,
1951
+ secret: process.env.IDENTITY_HEADER,
2283
1952
  }),
2284
1953
  };
2285
1954
  }
2286
1955
  /**
2287
- * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
1956
+ * Defines how to determine whether the Azure Service Fabric MSI is available, and also how to retrieve a token from the Azure Service Fabric MSI.
2288
1957
  */
2289
- const appServiceMsi2017 = {
1958
+ const fabricMsi = {
2290
1959
  async isAvailable({ scopes }) {
2291
1960
  const resource = mapScopesToResource(scopes);
2292
1961
  if (!resource) {
2293
- logger$c.info(`${msiName$6}: Unavailable. Multiple scopes are not supported.`);
1962
+ logger$e.info(`${msiName$1}: Unavailable. Multiple scopes are not supported.`);
2294
1963
  return false;
2295
1964
  }
2296
1965
  const env = process.env;
2297
- const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
1966
+ const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER && env.IDENTITY_SERVER_THUMBPRINT);
2298
1967
  if (!result) {
2299
- logger$c.info(`${msiName$6}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`);
1968
+ logger$e.info(`${msiName$1}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT, IDENTITY_HEADER and IDENTITY_SERVER_THUMBPRINT`);
2300
1969
  }
2301
1970
  return result;
2302
1971
  },
2303
1972
  async getToken(configuration, getTokenOptions = {}) {
2304
- const { identityClient, scopes, clientId, resourceId } = configuration;
1973
+ const { scopes, identityClient, clientId, resourceId } = configuration;
2305
1974
  if (resourceId) {
2306
- logger$c.warning(`${msiName$6}: managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
1975
+ logger$e.warning(`${msiName$1}: user defined managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
2307
1976
  }
2308
- logger$c.info(`${msiName$6}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
2309
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$6(scopes, clientId)), {
2310
- // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2311
- allowInsecureConnection: true }));
2312
- const tokenResponse = await identityClient.sendTokenRequest(request, expiresOnParser$3);
1977
+ logger$e.info([
1978
+ `${msiName$1}:`,
1979
+ "Using the endpoint and the secret coming from the environment variables:",
1980
+ `IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT},`,
1981
+ "IDENTITY_HEADER=[REDACTED] and",
1982
+ "IDENTITY_SERVER_THUMBPRINT=[REDACTED].",
1983
+ ].join(" "));
1984
+ const request = coreRestPipeline.createPipelineRequest(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$1(scopes, clientId, resourceId)));
1985
+ request.agent = new https__default["default"].Agent({
1986
+ // This is necessary because Service Fabric provides a self-signed certificate.
1987
+ // The alternative path is to verify the certificate using the IDENTITY_SERVER_THUMBPRINT env variable.
1988
+ rejectUnauthorized: false,
1989
+ });
1990
+ const tokenResponse = await identityClient.sendTokenRequest(request);
2313
1991
  return (tokenResponse && tokenResponse.accessToken) || null;
2314
1992
  },
2315
1993
  };
2316
1994
 
2317
1995
  // Copyright (c) Microsoft Corporation.
2318
- const msiName$5 = "ManagedIdentityCredential - CloudShellMSI";
2319
- const logger$b = credentialLogger(msiName$5);
1996
+ const msiName = "ManagedIdentityCredential - AppServiceMSI 2019";
1997
+ const logger$d = credentialLogger(msiName);
2320
1998
  /**
2321
1999
  * Generates the options used on the request for an access token.
2322
2000
  */
2323
- function prepareRequestOptions$5(scopes, clientId, resourceId) {
2001
+ function prepareRequestOptions(scopes, clientId, resourceId) {
2324
2002
  const resource = mapScopesToResource(scopes);
2325
2003
  if (!resource) {
2326
- throw new Error(`${msiName$5}: Multiple scopes are not supported.`);
2004
+ throw new Error(`${msiName}: Multiple scopes are not supported.`);
2327
2005
  }
2328
- const body = {
2006
+ const queryParameters = {
2329
2007
  resource,
2008
+ "api-version": "2019-08-01",
2330
2009
  };
2331
2010
  if (clientId) {
2332
- body.client_id = clientId;
2011
+ queryParameters.client_id = clientId;
2333
2012
  }
2334
2013
  if (resourceId) {
2335
- body.msi_res_id = resourceId;
2014
+ queryParameters.mi_res_id = resourceId;
2336
2015
  }
2016
+ const query = new URLSearchParams(queryParameters);
2337
2017
  // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2338
- if (!process.env.MSI_ENDPOINT) {
2339
- throw new Error(`${msiName$5}: Missing environment variable: MSI_ENDPOINT`);
2018
+ if (!process.env.IDENTITY_ENDPOINT) {
2019
+ throw new Error(`${msiName}: Missing environment variable: IDENTITY_ENDPOINT`);
2020
+ }
2021
+ if (!process.env.IDENTITY_HEADER) {
2022
+ throw new Error(`${msiName}: Missing environment variable: IDENTITY_HEADER`);
2340
2023
  }
2341
- const params = new URLSearchParams(body);
2342
2024
  return {
2343
- url: process.env.MSI_ENDPOINT,
2344
- method: "POST",
2345
- body: params.toString(),
2025
+ url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
2026
+ method: "GET",
2346
2027
  headers: coreRestPipeline.createHttpHeaders({
2347
2028
  Accept: "application/json",
2348
- Metadata: "true",
2349
- "Content-Type": "application/x-www-form-urlencoded",
2029
+ "X-IDENTITY-HEADER": process.env.IDENTITY_HEADER,
2350
2030
  }),
2351
2031
  };
2352
2032
  }
2353
2033
  /**
2354
- * Defines how to determine whether the Azure Cloud Shell MSI is available, and also how to retrieve a token from the Azure Cloud Shell MSI.
2355
- * Since Azure Managed Identities aren't available in the Azure Cloud Shell, we log a warning for users that try to access cloud shell using user assigned identity.
2034
+ * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
2356
2035
  */
2357
- const cloudShellMsi = {
2036
+ const appServiceMsi2019 = {
2358
2037
  async isAvailable({ scopes }) {
2359
2038
  const resource = mapScopesToResource(scopes);
2360
2039
  if (!resource) {
2361
- logger$b.info(`${msiName$5}: Unavailable. Multiple scopes are not supported.`);
2040
+ logger$d.info(`${msiName}: Unavailable. Multiple scopes are not supported.`);
2362
2041
  return false;
2363
2042
  }
2364
- const result = Boolean(process.env.MSI_ENDPOINT);
2043
+ const env = process.env;
2044
+ const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER);
2365
2045
  if (!result) {
2366
- logger$b.info(`${msiName$5}: Unavailable. The environment variable MSI_ENDPOINT is needed.`);
2046
+ logger$d.info(`${msiName}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT and IDENTITY_HEADER.`);
2047
+ }
2048
+ return result;
2049
+ },
2050
+ async getToken(configuration, getTokenOptions = {}) {
2051
+ const { identityClient, scopes, clientId, resourceId } = configuration;
2052
+ logger$d.info(`${msiName}: Using the endpoint and the secret coming form the environment variables: IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT} and IDENTITY_HEADER=[REDACTED].`);
2053
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions(scopes, clientId, resourceId)), {
2054
+ // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2055
+ allowInsecureConnection: true }));
2056
+ const tokenResponse = await identityClient.sendTokenRequest(request);
2057
+ return (tokenResponse && tokenResponse.accessToken) || null;
2058
+ },
2059
+ };
2060
+
2061
+ // Copyright (c) Microsoft Corporation.
2062
+ const logger$c = credentialLogger("ManagedIdentityCredential");
2063
+ /**
2064
+ * Attempts authentication using a managed identity available at the deployment environment.
2065
+ * This authentication type works in Azure VMs, App Service instances, Azure Functions applications,
2066
+ * Azure Kubernetes Services, Azure Service Fabric instances and inside of the Azure Cloud Shell.
2067
+ *
2068
+ * More information about configuring managed identities can be found here:
2069
+ * https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
2070
+ */
2071
+ class ManagedIdentityCredential {
2072
+ /**
2073
+ * @internal
2074
+ * @hidden
2075
+ */
2076
+ constructor(clientIdOrOptions, options) {
2077
+ this.isEndpointUnavailable = null;
2078
+ let _options;
2079
+ if (typeof clientIdOrOptions === "string") {
2080
+ this.clientId = clientIdOrOptions;
2081
+ _options = options;
2082
+ }
2083
+ else {
2084
+ this.clientId = clientIdOrOptions === null || clientIdOrOptions === void 0 ? void 0 : clientIdOrOptions.clientId;
2085
+ _options = clientIdOrOptions;
2086
+ }
2087
+ this.resourceId = _options === null || _options === void 0 ? void 0 : _options.resourceId;
2088
+ // For JavaScript users.
2089
+ if (this.clientId && this.resourceId) {
2090
+ throw new Error(`${ManagedIdentityCredential.name} - Client Id and Resource Id can't be provided at the same time.`);
2091
+ }
2092
+ this.identityClient = new IdentityClient(_options);
2093
+ this.isAvailableIdentityClient = new IdentityClient(Object.assign(Object.assign({}, _options), { retryOptions: {
2094
+ maxRetries: 0,
2095
+ } }));
2096
+ }
2097
+ async cachedAvailableMSI(scopes, getTokenOptions) {
2098
+ if (this.cachedMSI) {
2099
+ return this.cachedMSI;
2100
+ }
2101
+ const MSIs = [
2102
+ arcMsi,
2103
+ fabricMsi,
2104
+ appServiceMsi2019,
2105
+ appServiceMsi2017,
2106
+ cloudShellMsi,
2107
+ tokenExchangeMsi(),
2108
+ imdsMsi,
2109
+ ];
2110
+ for (const msi of MSIs) {
2111
+ if (await msi.isAvailable({
2112
+ scopes,
2113
+ identityClient: this.isAvailableIdentityClient,
2114
+ clientId: this.clientId,
2115
+ resourceId: this.resourceId,
2116
+ getTokenOptions,
2117
+ })) {
2118
+ this.cachedMSI = msi;
2119
+ return msi;
2120
+ }
2121
+ }
2122
+ throw new CredentialUnavailableError(`${ManagedIdentityCredential.name} - No MSI credential available`);
2123
+ }
2124
+ async authenticateManagedIdentity(scopes, getTokenOptions) {
2125
+ const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.authenticateManagedIdentity`, getTokenOptions);
2126
+ try {
2127
+ // Determining the available MSI, and avoiding checking for other MSIs while the program is running.
2128
+ const availableMSI = await this.cachedAvailableMSI(scopes, updatedOptions);
2129
+ return availableMSI.getToken({
2130
+ identityClient: this.identityClient,
2131
+ scopes,
2132
+ clientId: this.clientId,
2133
+ resourceId: this.resourceId,
2134
+ }, updatedOptions);
2135
+ }
2136
+ catch (err) {
2137
+ span.setStatus({
2138
+ status: "error",
2139
+ error: err,
2140
+ });
2141
+ throw err;
2142
+ }
2143
+ finally {
2144
+ span.end();
2145
+ }
2146
+ }
2147
+ /**
2148
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2149
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2150
+ * If an unexpected error occurs, an {@link AuthenticationError} will be thrown with the details of the failure.
2151
+ *
2152
+ * @param scopes - The list of scopes for which the token will have access.
2153
+ * @param options - The options used to configure any requests this
2154
+ * TokenCredential implementation might make.
2155
+ */
2156
+ async getToken(scopes, options) {
2157
+ let result = null;
2158
+ const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.getToken`, options);
2159
+ try {
2160
+ // isEndpointAvailable can be true, false, or null,
2161
+ // If it's null, it means we don't yet know whether
2162
+ // the endpoint is available and need to check for it.
2163
+ if (this.isEndpointUnavailable !== true) {
2164
+ result = await this.authenticateManagedIdentity(scopes, updatedOptions);
2165
+ if (result === null) {
2166
+ // If authenticateManagedIdentity returns null,
2167
+ // it means no MSI endpoints are available.
2168
+ // If so, we avoid trying to reach to them in future requests.
2169
+ this.isEndpointUnavailable = true;
2170
+ // It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method),
2171
+ // yet we had no access token. For this reason, we'll throw once with a specific message:
2172
+ const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
2173
+ logger$c.getToken.info(formatError(scopes, error));
2174
+ throw error;
2175
+ }
2176
+ // Since `authenticateManagedIdentity` didn't throw, and the result was not null,
2177
+ // We will assume that this endpoint is reachable from this point forward,
2178
+ // and avoid pinging again to it.
2179
+ this.isEndpointUnavailable = false;
2180
+ }
2181
+ else {
2182
+ // We've previously determined that the endpoint was unavailable,
2183
+ // either because it was unreachable or permanently unable to authenticate.
2184
+ const error = new CredentialUnavailableError("The managed identity endpoint is not currently available");
2185
+ logger$c.getToken.info(formatError(scopes, error));
2186
+ throw error;
2187
+ }
2188
+ logger$c.getToken.info(formatSuccess(scopes));
2189
+ return result;
2367
2190
  }
2368
- return result;
2369
- },
2370
- async getToken(configuration, getTokenOptions = {}) {
2371
- const { identityClient, scopes, clientId, resourceId } = configuration;
2372
- if (clientId) {
2373
- logger$b.warning(`${msiName$5}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
2191
+ catch (err) {
2192
+ // CredentialUnavailable errors are expected to reach here.
2193
+ // We intend them to bubble up, so that DefaultAzureCredential can catch them.
2194
+ if (err.name === "AuthenticationRequiredError") {
2195
+ throw err;
2196
+ }
2197
+ // Expected errors to reach this point:
2198
+ // - Errors coming from a method unexpectedly breaking.
2199
+ // - When identityClient.sendTokenRequest throws, in which case
2200
+ // if the status code was 400, it means that the endpoint is working,
2201
+ // but no identity is available.
2202
+ span.setStatus({
2203
+ status: "error",
2204
+ error: err,
2205
+ });
2206
+ // If either the network is unreachable,
2207
+ // we can safely assume the credential is unavailable.
2208
+ if (err.code === "ENETUNREACH") {
2209
+ const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. Network unreachable. Message: ${err.message}`);
2210
+ logger$c.getToken.info(formatError(scopes, error));
2211
+ throw error;
2212
+ }
2213
+ // If either the host was unreachable,
2214
+ // we can safely assume the credential is unavailable.
2215
+ if (err.code === "EHOSTUNREACH") {
2216
+ const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. No managed identity endpoint found. Message: ${err.message}`);
2217
+ logger$c.getToken.info(formatError(scopes, error));
2218
+ throw error;
2219
+ }
2220
+ // If err.statusCode has a value of 400, it comes from sendTokenRequest,
2221
+ // and it means that the endpoint is working, but that no identity is available.
2222
+ if (err.statusCode === 400) {
2223
+ throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`);
2224
+ }
2225
+ // If the error has no status code, we can assume there was no available identity.
2226
+ // This will throw silently during any ChainedTokenCredential.
2227
+ if (err.statusCode === undefined) {
2228
+ throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Authentication failed. Message ${err.message}`);
2229
+ }
2230
+ // Any other error should break the chain.
2231
+ throw new AuthenticationError(err.statusCode, {
2232
+ error: `${ManagedIdentityCredential.name} authentication failed.`,
2233
+ error_description: err.message,
2234
+ });
2374
2235
  }
2375
- if (resourceId) {
2376
- logger$b.warning(`${msiName$5}: user defined managed Identity by resource Id not supported. The argument resourceId might be ignored by the service.`);
2236
+ finally {
2237
+ // Finally is always called, both if we return and if we throw in the above try/catch.
2238
+ span.end();
2377
2239
  }
2378
- logger$b.info(`${msiName$5}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`);
2379
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$5(scopes, clientId, resourceId)), {
2380
- // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2381
- allowInsecureConnection: true }));
2382
- const tokenResponse = await identityClient.sendTokenRequest(request);
2383
- return (tokenResponse && tokenResponse.accessToken) || null;
2384
- },
2385
- };
2240
+ }
2241
+ }
2386
2242
 
2387
2243
  // Copyright (c) Microsoft Corporation.
2388
- const msiName$4 = "ManagedIdentityCredential - IMDS";
2389
- const logger$a = credentialLogger(msiName$4);
2390
2244
  /**
2391
- * Formats the expiration date of the received token into the number of milliseconds between that date and midnight, January 1, 1970.
2245
+ * Ensures the scopes value is an array.
2246
+ * @internal
2392
2247
  */
2393
- function expiresOnParser$2(requestBody) {
2394
- if (requestBody.expires_on) {
2395
- // Use the expires_on timestamp if it's available
2396
- const expires = +requestBody.expires_on * 1000;
2397
- logger$a.info(`${msiName$4}: Using expires_on: ${expires} (original value: ${requestBody.expires_on})`);
2398
- return expires;
2399
- }
2400
- else {
2401
- // If these aren't possible, use expires_in and calculate a timestamp
2402
- const expires = Date.now() + requestBody.expires_in * 1000;
2403
- logger$a.info(`${msiName$4}: IMDS using expires_in: ${expires} (original value: ${requestBody.expires_in})`);
2404
- return expires;
2405
- }
2248
+ function ensureScopes(scopes) {
2249
+ return Array.isArray(scopes) ? scopes : [scopes];
2406
2250
  }
2407
2251
  /**
2408
- * Generates the options used on the request for an access token.
2252
+ * Throws if the received scope is not valid.
2253
+ * @internal
2409
2254
  */
2410
- function prepareRequestOptions$4(scopes, clientId, resourceId, options) {
2411
- var _a;
2412
- const resource = mapScopesToResource(scopes);
2413
- if (!resource) {
2414
- throw new Error(`${msiName$4}: Multiple scopes are not supported.`);
2415
- }
2416
- const { skipQuery, skipMetadataHeader } = options || {};
2417
- let query = "";
2418
- // Pod Identity will try to process this request even if the Metadata header is missing.
2419
- // We can exclude the request query to ensure no IMDS endpoint tries to process the ping request.
2420
- if (!skipQuery) {
2421
- const queryParameters = {
2422
- resource,
2423
- "api-version": imdsApiVersion,
2424
- };
2425
- if (clientId) {
2426
- queryParameters.client_id = clientId;
2427
- }
2428
- if (resourceId) {
2429
- queryParameters.msi_res_id = resourceId;
2430
- }
2431
- const params = new URLSearchParams(queryParameters);
2432
- query = `?${params.toString()}`;
2433
- }
2434
- const url = new URL(imdsEndpointPath, (_a = process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : imdsHost);
2435
- const rawHeaders = {
2436
- Accept: "application/json",
2437
- Metadata: "true",
2438
- };
2439
- // Remove the Metadata header to invoke a request error from some IMDS endpoints.
2440
- if (skipMetadataHeader) {
2441
- delete rawHeaders.Metadata;
2255
+ function ensureValidScope(scope, logger) {
2256
+ if (!scope.match(/^[0-9a-zA-Z-.:/]+$/)) {
2257
+ const error = new Error("Invalid scope was specified by the user or calling client");
2258
+ logger.getToken.info(formatError(scope, error));
2259
+ throw error;
2442
2260
  }
2443
- return {
2444
- // In this case, the `?` should be added in the "query" variable `skipQuery` is not set.
2445
- url: `${url}${query}`,
2446
- method: "GET",
2447
- headers: coreRestPipeline.createHttpHeaders(rawHeaders),
2448
- };
2449
2261
  }
2450
- // 800ms -> 1600ms -> 3200ms
2451
- const imdsMsiRetryConfig = {
2452
- maxRetries: 3,
2453
- startDelayInMs: 800,
2454
- intervalIncrement: 2,
2455
- };
2456
2262
  /**
2457
- * Defines how to determine whether the Azure IMDS MSI is available, and also how to retrieve a token from the Azure IMDS MSI.
2263
+ * Returns the resource out of a scope.
2264
+ * @internal
2458
2265
  */
2459
- const imdsMsi = {
2460
- async isAvailable({ scopes, identityClient, clientId, resourceId, getTokenOptions = {}, }) {
2461
- const resource = mapScopesToResource(scopes);
2462
- if (!resource) {
2463
- logger$a.info(`${msiName$4}: Unavailable. Multiple scopes are not supported.`);
2464
- return false;
2266
+ function getScopeResource(scope) {
2267
+ return scope.replace(/\/.default$/, "");
2268
+ }
2269
+
2270
+ // Copyright (c) Microsoft Corporation.
2271
+ /**
2272
+ * Mockable reference to the CLI credential cliCredentialFunctions
2273
+ * @internal
2274
+ */
2275
+ const cliCredentialInternals = {
2276
+ /**
2277
+ * @internal
2278
+ */
2279
+ getSafeWorkingDir() {
2280
+ if (process.platform === "win32") {
2281
+ if (!process.env.SystemRoot) {
2282
+ throw new Error("Azure CLI credential expects a 'SystemRoot' environment variable");
2283
+ }
2284
+ return process.env.SystemRoot;
2465
2285
  }
2466
- // if the PodIdentityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist
2467
- if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
2468
- return true;
2286
+ else {
2287
+ return "/bin";
2469
2288
  }
2470
- if (!identityClient) {
2471
- throw new Error("Missing IdentityClient");
2289
+ },
2290
+ /**
2291
+ * Gets the access token from Azure CLI
2292
+ * @param resource - The resource to use when getting the token
2293
+ * @internal
2294
+ */
2295
+ async getAzureCliAccessToken(resource, tenantId) {
2296
+ let tenantSection = [];
2297
+ if (tenantId) {
2298
+ tenantSection = ["--tenant", tenantId];
2472
2299
  }
2473
- const requestOptions = prepareRequestOptions$4(resource, clientId, resourceId, {
2474
- skipMetadataHeader: true,
2475
- skipQuery: true,
2476
- });
2477
- return tracingClient.withSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions, async (options) => {
2478
- var _a;
2479
- requestOptions.tracingOptions = options.tracingOptions;
2480
- // Create a request with a timeout since we expect that
2481
- // not having a "Metadata" header should cause an error to be
2482
- // returned quickly from the endpoint, proving its availability.
2483
- const request = coreRestPipeline.createPipelineRequest(requestOptions);
2484
- // Default to 300 if the default of 0 is used.
2485
- // Negative values can still be used to disable the timeout.
2486
- request.timeout = ((_a = options.requestOptions) === null || _a === void 0 ? void 0 : _a.timeout) || 300;
2487
- // This MSI uses the imdsEndpoint to get the token, which only uses http://
2488
- request.allowInsecureConnection = true;
2300
+ return new Promise((resolve, reject) => {
2489
2301
  try {
2490
- logger$a.info(`${msiName$4}: Pinging the Azure IMDS endpoint`);
2491
- await identityClient.sendRequest(request);
2302
+ child_process__default["default"].execFile("az", [
2303
+ "account",
2304
+ "get-access-token",
2305
+ "--output",
2306
+ "json",
2307
+ "--resource",
2308
+ resource,
2309
+ ...tenantSection,
2310
+ ], { cwd: cliCredentialInternals.getSafeWorkingDir(), shell: true }, (error, stdout, stderr) => {
2311
+ resolve({ stdout: stdout, stderr: stderr, error });
2312
+ });
2492
2313
  }
2493
2314
  catch (err) {
2494
- // If the request failed, or Node.js was unable to establish a connection,
2495
- // or the host was down, we'll assume the IMDS endpoint isn't available.
2496
- if (coreUtil.isError(err)) {
2497
- logger$a.verbose(`${msiName$4}: Caught error ${err.name}: ${err.message}`);
2498
- }
2499
- logger$a.info(`${msiName$4}: The Azure IMDS endpoint is unavailable`);
2500
- return false;
2315
+ reject(err);
2501
2316
  }
2502
- // If we received any response, the endpoint is available
2503
- logger$a.info(`${msiName$4}: The Azure IMDS endpoint is available`);
2504
- return true;
2505
2317
  });
2506
2318
  },
2507
- async getToken(configuration, getTokenOptions = {}) {
2508
- const { identityClient, scopes, clientId, resourceId } = configuration;
2509
- if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
2510
- logger$a.info(`${msiName$4}: Using the Azure IMDS endpoint coming from the environment variable AZURE_POD_IDENTITY_AUTHORITY_HOST=${process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST}.`);
2511
- }
2512
- else {
2513
- logger$a.info(`${msiName$4}: Using the default Azure IMDS endpoint ${imdsHost}.`);
2514
- }
2515
- let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;
2516
- for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {
2319
+ };
2320
+ const logger$b = credentialLogger("AzureCliCredential");
2321
+ /**
2322
+ * This credential will use the currently logged-in user login information
2323
+ * via the Azure CLI ('az') commandline tool.
2324
+ * To do so, it will read the user access token and expire time
2325
+ * with Azure CLI command "az account get-access-token".
2326
+ */
2327
+ class AzureCliCredential {
2328
+ /**
2329
+ * Creates an instance of the {@link AzureCliCredential}.
2330
+ *
2331
+ * To use this credential, ensure that you have already logged
2332
+ * in via the 'az' tool using the command "az login" from the commandline.
2333
+ *
2334
+ * @param options - Options, to optionally allow multi-tenant requests.
2335
+ */
2336
+ constructor(options) {
2337
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
2338
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
2339
+ }
2340
+ /**
2341
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2342
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2343
+ *
2344
+ * @param scopes - The list of scopes for which the token will have access.
2345
+ * @param options - The options used to configure any requests this
2346
+ * TokenCredential implementation might make.
2347
+ */
2348
+ async getToken(scopes, options = {}) {
2349
+ const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds);
2350
+ const scope = typeof scopes === "string" ? scopes : scopes[0];
2351
+ logger$b.getToken.info(`Using the scope ${scope}`);
2352
+ ensureValidScope(scope, logger$b);
2353
+ const resource = getScopeResource(scope);
2354
+ return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => {
2355
+ var _a, _b, _c, _d;
2517
2356
  try {
2518
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$4(scopes, clientId, resourceId)), { allowInsecureConnection: true }));
2519
- const tokenResponse = await identityClient.sendTokenRequest(request, expiresOnParser$2);
2520
- return (tokenResponse && tokenResponse.accessToken) || null;
2521
- }
2522
- catch (error) {
2523
- if (error.statusCode === 404) {
2524
- await coreUtil.delay(nextDelayInMs);
2525
- nextDelayInMs *= imdsMsiRetryConfig.intervalIncrement;
2526
- continue;
2357
+ const obj = await cliCredentialInternals.getAzureCliAccessToken(resource, tenantId);
2358
+ const specificScope = (_a = obj.stderr) === null || _a === void 0 ? void 0 : _a.match("(.*)az login --scope(.*)");
2359
+ const isLoginError = ((_b = obj.stderr) === null || _b === void 0 ? void 0 : _b.match("(.*)az login(.*)")) && !specificScope;
2360
+ const isNotInstallError = ((_c = obj.stderr) === null || _c === void 0 ? void 0 : _c.match("az:(.*)not found")) || ((_d = obj.stderr) === null || _d === void 0 ? void 0 : _d.startsWith("'az' is not recognized"));
2361
+ if (isNotInstallError) {
2362
+ const error = new CredentialUnavailableError("Azure CLI could not be found. Please visit https://aka.ms/azure-cli for installation instructions and then, once installed, authenticate to your Azure account using 'az login'.");
2363
+ logger$b.getToken.info(formatError(scopes, error));
2364
+ throw error;
2365
+ }
2366
+ if (isLoginError) {
2367
+ const error = new CredentialUnavailableError("Please run 'az login' from a command prompt to authenticate before using this credential.");
2368
+ logger$b.getToken.info(formatError(scopes, error));
2369
+ throw error;
2370
+ }
2371
+ try {
2372
+ const responseData = obj.stdout;
2373
+ const response = JSON.parse(responseData);
2374
+ logger$b.getToken.info(formatSuccess(scopes));
2375
+ const returnValue = {
2376
+ token: response.accessToken,
2377
+ expiresOnTimestamp: new Date(response.expiresOn).getTime(),
2378
+ };
2379
+ return returnValue;
2380
+ }
2381
+ catch (e) {
2382
+ if (obj.stderr) {
2383
+ throw new CredentialUnavailableError(obj.stderr);
2384
+ }
2385
+ throw e;
2527
2386
  }
2387
+ }
2388
+ catch (err) {
2389
+ const error = err.name === "CredentialUnavailableError"
2390
+ ? err
2391
+ : new CredentialUnavailableError(err.message || "Unknown error while trying to retrieve the access token");
2392
+ logger$b.getToken.info(formatError(scopes, error));
2528
2393
  throw error;
2529
2394
  }
2530
- }
2531
- throw new AuthenticationError(404, `${msiName$4}: Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`);
2395
+ });
2396
+ }
2397
+ }
2398
+
2399
+ // Copyright (c) Microsoft Corporation.
2400
+ /**
2401
+ * Easy to mock childProcess utils.
2402
+ * @internal
2403
+ */
2404
+ const processUtils = {
2405
+ /**
2406
+ * Promisifying childProcess.execFile
2407
+ * @internal
2408
+ */
2409
+ execFile(file, params, options) {
2410
+ return new Promise((resolve, reject) => {
2411
+ child_process__namespace.execFile(file, params, options, (error, stdout, stderr) => {
2412
+ if (Buffer.isBuffer(stdout)) {
2413
+ stdout = stdout.toString("utf8");
2414
+ }
2415
+ if (Buffer.isBuffer(stderr)) {
2416
+ stderr = stderr.toString("utf8");
2417
+ }
2418
+ if (stderr || error) {
2419
+ reject(stderr ? new Error(stderr) : error);
2420
+ }
2421
+ else {
2422
+ resolve(stdout);
2423
+ }
2424
+ });
2425
+ });
2532
2426
  },
2533
2427
  };
2534
2428
 
2535
2429
  // Copyright (c) Microsoft Corporation.
2536
- const msiName$3 = "ManagedIdentityCredential - Azure Arc MSI";
2537
- const logger$9 = credentialLogger(msiName$3);
2430
+ const logger$a = credentialLogger("AzurePowerShellCredential");
2431
+ const isWindows = process.platform === "win32";
2538
2432
  /**
2539
- * Generates the options used on the request for an access token.
2433
+ * Returns a platform-appropriate command name by appending ".exe" on Windows.
2434
+ *
2435
+ * @internal
2540
2436
  */
2541
- function prepareRequestOptions$3(scopes, clientId, resourceId) {
2542
- const resource = mapScopesToResource(scopes);
2543
- if (!resource) {
2544
- throw new Error(`${msiName$3}: Multiple scopes are not supported.`);
2545
- }
2546
- const queryParameters = {
2547
- resource,
2548
- "api-version": azureArcAPIVersion,
2549
- };
2550
- if (clientId) {
2551
- queryParameters.client_id = clientId;
2552
- }
2553
- if (resourceId) {
2554
- queryParameters.msi_res_id = resourceId;
2437
+ function formatCommand(commandName) {
2438
+ if (isWindows) {
2439
+ return `${commandName}.exe`;
2555
2440
  }
2556
- // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2557
- if (!process.env.IDENTITY_ENDPOINT) {
2558
- throw new Error(`${msiName$3}: Missing environment variable: IDENTITY_ENDPOINT`);
2441
+ else {
2442
+ return commandName;
2559
2443
  }
2560
- const query = new URLSearchParams(queryParameters);
2561
- return coreRestPipeline.createPipelineRequest({
2562
- // Should be similar to: http://localhost:40342/metadata/identity/oauth2/token
2563
- url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
2564
- method: "GET",
2565
- headers: coreRestPipeline.createHttpHeaders({
2566
- Accept: "application/json",
2567
- Metadata: "true",
2568
- }),
2569
- });
2570
2444
  }
2571
2445
  /**
2572
- * Retrieves the file contents at the given path using promises.
2573
- * Useful since `fs`'s readFileSync locks the thread, and to avoid extra dependencies.
2446
+ * Receives a list of commands to run, executes them, then returns the outputs.
2447
+ * If anything fails, an error is thrown.
2448
+ * @internal
2574
2449
  */
2575
- function readFileAsync$1(path, options) {
2576
- return new Promise((resolve, reject) => fs.readFile(path, options, (err, data) => {
2577
- if (err) {
2578
- reject(err);
2579
- }
2580
- resolve(data);
2581
- }));
2450
+ async function runCommands(commands) {
2451
+ const results = [];
2452
+ for (const command of commands) {
2453
+ const [file, ...parameters] = command;
2454
+ const result = (await processUtils.execFile(file, parameters, { encoding: "utf8" }));
2455
+ results.push(result);
2456
+ }
2457
+ return results;
2582
2458
  }
2583
2459
  /**
2584
- * Does a request to the authentication provider that results in a file path.
2460
+ * Known PowerShell errors
2461
+ * @internal
2585
2462
  */
2586
- async function filePathRequest(identityClient, requestPrepareOptions) {
2587
- const response = await identityClient.sendRequest(coreRestPipeline.createPipelineRequest(requestPrepareOptions));
2588
- if (response.status !== 401) {
2589
- let message = "";
2590
- if (response.bodyAsText) {
2591
- message = ` Response: ${response.bodyAsText}`;
2592
- }
2593
- throw new AuthenticationError(response.status, `${msiName$3}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`);
2594
- }
2595
- const authHeader = response.headers.get("www-authenticate") || "";
2596
- try {
2597
- return authHeader.split("=").slice(1)[0];
2598
- }
2599
- catch (e) {
2600
- throw Error(`Invalid www-authenticate header format: ${authHeader}`);
2601
- }
2602
- }
2463
+ const powerShellErrors = {
2464
+ login: "Run Connect-AzAccount to login",
2465
+ installed: "The specified module 'Az.Accounts' with version '2.2.0' was not loaded because no valid module file was found in any module directory",
2466
+ };
2603
2467
  /**
2604
- * Defines how to determine whether the Azure Arc MSI is available, and also how to retrieve a token from the Azure Arc MSI.
2468
+ * Messages to use when throwing in this credential.
2469
+ * @internal
2605
2470
  */
2606
- const arcMsi = {
2607
- async isAvailable({ scopes }) {
2608
- const resource = mapScopesToResource(scopes);
2609
- if (!resource) {
2610
- logger$9.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`);
2611
- return false;
2612
- }
2613
- const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
2614
- if (!result) {
2615
- logger$9.info(`${msiName$3}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`);
2616
- }
2617
- return result;
2618
- },
2619
- async getToken(configuration, getTokenOptions = {}) {
2620
- var _a;
2621
- const { identityClient, scopes, clientId, resourceId } = configuration;
2622
- if (clientId) {
2623
- logger$9.warning(`${msiName$3}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
2624
- }
2625
- if (resourceId) {
2626
- logger$9.warning(`${msiName$3}: user defined managed Identity by resource Id is not supported. Argument resourceId will be ignored.`);
2627
- }
2628
- logger$9.info(`${msiName$3}: Authenticating.`);
2629
- const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes, clientId, resourceId)), { allowInsecureConnection: true });
2630
- const filePath = await filePathRequest(identityClient, requestOptions);
2631
- if (!filePath) {
2632
- throw new Error(`${msiName$3}: Failed to find the token file.`);
2633
- }
2634
- const key = await readFileAsync$1(filePath, { encoding: "utf-8" });
2635
- (_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`);
2636
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({}, requestOptions), {
2637
- // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2638
- allowInsecureConnection: true }));
2639
- const tokenResponse = await identityClient.sendTokenRequest(request);
2640
- return (tokenResponse && tokenResponse.accessToken) || null;
2641
- },
2471
+ const powerShellPublicErrorMessages = {
2472
+ login: "Please run 'Connect-AzAccount' from PowerShell to authenticate before using this credential.",
2473
+ installed: `The 'Az.Account' module >= 2.2.0 is not installed. Install the Azure Az PowerShell module with: "Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force".`,
2474
+ troubleshoot: `To troubleshoot, visit https://aka.ms/azsdk/js/identity/powershellcredential/troubleshoot.`,
2642
2475
  };
2643
-
2644
- // Copyright (c) Microsoft Corporation.
2645
- const msiName$2 = "ManagedIdentityCredential - Token Exchange";
2646
- const logger$8 = credentialLogger(msiName$2);
2647
- const readFileAsync = util.promisify(fs__default["default"].readFile);
2476
+ // PowerShell Azure User not logged in error check.
2477
+ const isLoginError = (err) => err.message.match(`(.*)${powerShellErrors.login}(.*)`);
2478
+ // Az Module not Installed in Azure PowerShell check.
2479
+ const isNotInstalledError = (err) => err.message.match(powerShellErrors.installed);
2648
2480
  /**
2649
- * Generates the options used on the request for an access token.
2481
+ * The PowerShell commands to be tried, in order.
2482
+ *
2483
+ * @internal
2650
2484
  */
2651
- function prepareRequestOptions$2(scopes, clientAssertion, clientId) {
2652
- var _a;
2653
- const bodyParams = {
2654
- scope: Array.isArray(scopes) ? scopes.join(" ") : scopes,
2655
- client_assertion: clientAssertion,
2656
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
2657
- client_id: clientId,
2658
- grant_type: "client_credentials",
2659
- };
2660
- const urlParams = new URLSearchParams(bodyParams);
2661
- const url = new URL(`${process.env.AZURE_TENANT_ID}/oauth2/v2.0/token`, (_a = process.env.AZURE_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : DefaultAuthorityHost);
2662
- return {
2663
- url: url.toString(),
2664
- method: "POST",
2665
- body: urlParams.toString(),
2666
- headers: coreRestPipeline.createHttpHeaders({
2667
- Accept: "application/json",
2668
- }),
2669
- };
2485
+ const commandStack = [formatCommand("pwsh")];
2486
+ if (isWindows) {
2487
+ commandStack.push(formatCommand("powershell"));
2670
2488
  }
2671
2489
  /**
2672
- * Defines how to determine whether the token exchange MSI is available, and also how to retrieve a token from the token exchange MSI.
2490
+ * This credential will use the currently logged-in user information from the
2491
+ * Azure PowerShell module. To do so, it will read the user access token and
2492
+ * expire time with Azure PowerShell command `Get-AzAccessToken -ResourceUrl {ResourceScope}`
2673
2493
  */
2674
- function tokenExchangeMsi() {
2675
- const azureFederatedTokenFilePath = process.env.AZURE_FEDERATED_TOKEN_FILE;
2676
- let azureFederatedTokenFileContent = undefined;
2677
- let cacheDate = undefined;
2678
- // Only reads from the assertion file once every 5 minutes
2679
- async function readAssertion() {
2680
- // Cached assertions expire after 5 minutes
2681
- if (cacheDate !== undefined && Date.now() - cacheDate >= 1000 * 60 * 5) {
2682
- azureFederatedTokenFileContent = undefined;
2683
- }
2684
- if (!azureFederatedTokenFileContent) {
2685
- const file = await readFileAsync(azureFederatedTokenFilePath, "utf8");
2686
- const value = file.trim();
2687
- if (!value) {
2688
- throw new Error(`No content on the file ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
2494
+ class AzurePowerShellCredential {
2495
+ /**
2496
+ * Creates an instance of the {@link AzurePowerShellCredential}.
2497
+ *
2498
+ * To use this credential:
2499
+ * - Install the Azure Az PowerShell module with:
2500
+ * `Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force`.
2501
+ * - You have already logged in to Azure PowerShell using the command
2502
+ * `Connect-AzAccount` from the command line.
2503
+ *
2504
+ * @param options - Options, to optionally allow multi-tenant requests.
2505
+ */
2506
+ constructor(options) {
2507
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
2508
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
2509
+ }
2510
+ /**
2511
+ * Gets the access token from Azure PowerShell
2512
+ * @param resource - The resource to use when getting the token
2513
+ */
2514
+ async getAzurePowerShellAccessToken(resource, tenantId) {
2515
+ // Clone the stack to avoid mutating it while iterating
2516
+ for (const powerShellCommand of [...commandStack]) {
2517
+ try {
2518
+ await runCommands([[powerShellCommand, "/?"]]);
2519
+ }
2520
+ catch (e) {
2521
+ // Remove this credential from the original stack so that we don't try it again.
2522
+ commandStack.shift();
2523
+ continue;
2524
+ }
2525
+ let tenantSection = "";
2526
+ if (tenantId) {
2527
+ tenantSection = `-TenantId "${tenantId}"`;
2689
2528
  }
2690
- else {
2691
- azureFederatedTokenFileContent = value;
2692
- cacheDate = Date.now();
2529
+ const results = await runCommands([
2530
+ [
2531
+ powerShellCommand,
2532
+ "-Command",
2533
+ "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru",
2534
+ ],
2535
+ [
2536
+ powerShellCommand,
2537
+ "-Command",
2538
+ `Get-AzAccessToken ${tenantSection} -ResourceUrl "${resource}" | ConvertTo-Json`,
2539
+ ],
2540
+ ]);
2541
+ const result = results[1];
2542
+ try {
2543
+ return JSON.parse(result);
2544
+ }
2545
+ catch (e) {
2546
+ throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
2693
2547
  }
2694
2548
  }
2695
- return azureFederatedTokenFileContent;
2549
+ throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`);
2696
2550
  }
2697
- return {
2698
- async isAvailable({ clientId }) {
2699
- const env = process.env;
2700
- const result = Boolean((clientId || env.AZURE_CLIENT_ID) && env.AZURE_TENANT_ID && azureFederatedTokenFilePath);
2701
- if (!result) {
2702
- logger$8.info(`${msiName$2}: Unavailable. The environment variables needed are: AZURE_CLIENT_ID (or the client ID sent through the parameters), AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE`);
2703
- }
2704
- return result;
2705
- },
2706
- async getToken(configuration, getTokenOptions = {}) {
2707
- const { identityClient, scopes, clientId } = configuration;
2708
- logger$8.info(`${msiName$2}: Using the client assertion coming from environment variables.`);
2709
- let assertion;
2551
+ /**
2552
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2553
+ * If the authentication cannot be performed through PowerShell, a {@link CredentialUnavailableError} will be thrown.
2554
+ *
2555
+ * @param scopes - The list of scopes for which the token will have access.
2556
+ * @param options - The options used to configure any requests this TokenCredential implementation might make.
2557
+ */
2558
+ async getToken(scopes, options = {}) {
2559
+ return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => {
2560
+ const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds);
2561
+ const scope = typeof scopes === "string" ? scopes : scopes[0];
2562
+ ensureValidScope(scope, logger$a);
2563
+ logger$a.getToken.info(`Using the scope ${scope}`);
2564
+ const resource = getScopeResource(scope);
2710
2565
  try {
2711
- assertion = await readAssertion();
2566
+ const response = await this.getAzurePowerShellAccessToken(resource, tenantId);
2567
+ logger$a.getToken.info(formatSuccess(scopes));
2568
+ return {
2569
+ token: response.Token,
2570
+ expiresOnTimestamp: new Date(response.ExpiresOn).getTime(),
2571
+ };
2712
2572
  }
2713
2573
  catch (err) {
2714
- throw new Error(`${msiName$2}: Failed to read ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
2574
+ if (isNotInstalledError(err)) {
2575
+ const error = new CredentialUnavailableError(powerShellPublicErrorMessages.installed);
2576
+ logger$a.getToken.info(formatError(scope, error));
2577
+ throw error;
2578
+ }
2579
+ else if (isLoginError(err)) {
2580
+ const error = new CredentialUnavailableError(powerShellPublicErrorMessages.login);
2581
+ logger$a.getToken.info(formatError(scope, error));
2582
+ throw error;
2583
+ }
2584
+ const error = new CredentialUnavailableError(`${err}. ${powerShellPublicErrorMessages.troubleshoot}`);
2585
+ logger$a.getToken.info(formatError(scope, error));
2586
+ throw error;
2715
2587
  }
2716
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$2(scopes, assertion, clientId || process.env.AZURE_CLIENT_ID)), {
2717
- // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2718
- allowInsecureConnection: true }));
2719
- const tokenResponse = await identityClient.sendTokenRequest(request);
2720
- return (tokenResponse && tokenResponse.accessToken) || null;
2721
- },
2722
- };
2588
+ });
2589
+ }
2723
2590
  }
2724
2591
 
2725
2592
  // Copyright (c) Microsoft Corporation.
2726
- // This MSI can be easily tested by deploying a container to Azure Service Fabric with the Dockerfile:
2727
- //
2728
- // FROM node:12
2729
- // RUN wget https://host.any/path/bash.sh
2730
- // CMD ["bash", "bash.sh"]
2731
- //
2732
- // Where the bash script contains:
2733
- //
2734
- // curl --insecure $IDENTITY_ENDPOINT'?api-version=2019-07-01-preview&resource=https://vault.azure.net/' -H "Secret: $IDENTITY_HEADER"
2735
- //
2736
- const msiName$1 = "ManagedIdentityCredential - Fabric MSI";
2737
- const logger$7 = credentialLogger(msiName$1);
2738
2593
  /**
2739
- * Formats the expiration date of the received token into the number of milliseconds between that date and midnight, January 1, 1970.
2594
+ * @internal
2595
+ */
2596
+ const logger$9 = credentialLogger("ChainedTokenCredential");
2597
+ /**
2598
+ * Enables multiple `TokenCredential` implementations to be tried in order
2599
+ * until one of the getToken methods returns an access token.
2740
2600
  */
2741
- function expiresOnParser$1(requestBody) {
2742
- // Parses a string representation of the milliseconds since epoch into a number value
2743
- return Number(requestBody.expires_on);
2601
+ class ChainedTokenCredential {
2602
+ /**
2603
+ * Creates an instance of ChainedTokenCredential using the given credentials.
2604
+ *
2605
+ * @param sources - `TokenCredential` implementations to be tried in order.
2606
+ *
2607
+ * Example usage:
2608
+ * ```javascript
2609
+ * const firstCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
2610
+ * const secondCredential = new ClientSecretCredential(tenantId, anotherClientId, anotherSecret);
2611
+ * const credentialChain = new ChainedTokenCredential(firstCredential, secondCredential);
2612
+ * ```
2613
+ */
2614
+ constructor(...sources) {
2615
+ /**
2616
+ * The message to use when the chained token fails to get a token
2617
+ */
2618
+ this.UnavailableMessage = "ChainedTokenCredential => failed to retrieve a token from the included credentials";
2619
+ this._sources = [];
2620
+ this._sources = sources;
2621
+ }
2622
+ /**
2623
+ * Returns the first access token returned by one of the chained
2624
+ * `TokenCredential` implementations. Throws an {@link AggregateAuthenticationError}
2625
+ * when one or more credentials throws an {@link AuthenticationError} and
2626
+ * no credentials have returned an access token.
2627
+ *
2628
+ * This method is called automatically by Azure SDK client libraries. You may call this method
2629
+ * directly, but you must also handle token caching and token refreshing.
2630
+ *
2631
+ * @param scopes - The list of scopes for which the token will have access.
2632
+ * @param options - The options used to configure any requests this
2633
+ * `TokenCredential` implementation might make.
2634
+ */
2635
+ async getToken(scopes, options = {}) {
2636
+ let token = null;
2637
+ let successfulCredentialName = "";
2638
+ const errors = [];
2639
+ return tracingClient.withSpan("ChainedTokenCredential.getToken", options, async (updatedOptions) => {
2640
+ for (let i = 0; i < this._sources.length && token === null; i++) {
2641
+ try {
2642
+ token = await this._sources[i].getToken(scopes, updatedOptions);
2643
+ successfulCredentialName = this._sources[i].constructor.name;
2644
+ }
2645
+ catch (err) {
2646
+ if (err.name === "CredentialUnavailableError" ||
2647
+ err.name === "AuthenticationRequiredError") {
2648
+ errors.push(err);
2649
+ }
2650
+ else {
2651
+ logger$9.getToken.info(formatError(scopes, err));
2652
+ throw err;
2653
+ }
2654
+ }
2655
+ }
2656
+ if (!token && errors.length > 0) {
2657
+ const err = new AggregateAuthenticationError(errors, "ChainedTokenCredential authentication failed.");
2658
+ logger$9.getToken.info(formatError(scopes, err));
2659
+ throw err;
2660
+ }
2661
+ logger$9.getToken.info(`Result for ${successfulCredentialName}: ${formatSuccess(scopes)}`);
2662
+ if (token === null) {
2663
+ throw new CredentialUnavailableError("Failed to retrieve a valid token");
2664
+ }
2665
+ return token;
2666
+ });
2667
+ }
2744
2668
  }
2669
+
2670
+ // Copyright (c) Microsoft Corporation.
2671
+ const readFileAsync = util.promisify(fs.readFile);
2745
2672
  /**
2746
- * Generates the options used on the request for an access token.
2673
+ * Tries to asynchronously load a certificate from the given path.
2674
+ *
2675
+ * @param configuration - Either the PEM value or the path to the certificate.
2676
+ * @param sendCertificateChain - Option to include x5c header for SubjectName and Issuer name authorization.
2677
+ * @returns - The certificate parts, or `undefined` if the certificate could not be loaded.
2678
+ * @internal
2747
2679
  */
2748
- function prepareRequestOptions$1(scopes, clientId, resourceId) {
2749
- const resource = mapScopesToResource(scopes);
2750
- if (!resource) {
2751
- throw new Error(`${msiName$1}: Multiple scopes are not supported.`);
2680
+ async function parseCertificate(configuration, sendCertificateChain) {
2681
+ const certificateParts = {};
2682
+ const certificate = configuration
2683
+ .certificate;
2684
+ const certificatePath = configuration
2685
+ .certificatePath;
2686
+ certificateParts.certificateContents =
2687
+ certificate || (await readFileAsync(certificatePath, "utf8"));
2688
+ if (sendCertificateChain) {
2689
+ certificateParts.x5c = certificateParts.certificateContents;
2752
2690
  }
2753
- const queryParameters = {
2754
- resource,
2755
- "api-version": azureFabricVersion,
2756
- };
2757
- if (clientId) {
2758
- queryParameters.client_id = clientId;
2691
+ const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
2692
+ const publicKeys = [];
2693
+ // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
2694
+ let match;
2695
+ do {
2696
+ match = certificatePattern.exec(certificateParts.certificateContents);
2697
+ if (match) {
2698
+ publicKeys.push(match[3]);
2699
+ }
2700
+ } while (match);
2701
+ if (publicKeys.length === 0) {
2702
+ throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
2759
2703
  }
2760
- if (resourceId) {
2761
- queryParameters.msi_res_id = resourceId;
2704
+ certificateParts.thumbprint = crypto.createHash("sha1")
2705
+ .update(Buffer.from(publicKeys[0], "base64"))
2706
+ .digest("hex")
2707
+ .toUpperCase();
2708
+ return certificateParts;
2709
+ }
2710
+ /**
2711
+ * MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
2712
+ * @internal
2713
+ */
2714
+ class MsalClientCertificate extends MsalNode {
2715
+ constructor(options) {
2716
+ super(options);
2717
+ this.requiresConfidential = true;
2718
+ this.configuration = options.configuration;
2719
+ this.sendCertificateChain = options.sendCertificateChain;
2762
2720
  }
2763
- const query = new URLSearchParams(queryParameters);
2764
- // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2765
- if (!process.env.IDENTITY_ENDPOINT) {
2766
- throw new Error("Missing environment variable: IDENTITY_ENDPOINT");
2721
+ // Changing the MSAL configuration asynchronously
2722
+ async init(options) {
2723
+ try {
2724
+ const parts = await parseCertificate(this.configuration, this.sendCertificateChain);
2725
+ let privateKey;
2726
+ if (this.configuration.certificatePassword !== undefined) {
2727
+ const privateKeyObject = crypto.createPrivateKey({
2728
+ key: parts.certificateContents,
2729
+ passphrase: this.configuration.certificatePassword,
2730
+ format: "pem",
2731
+ });
2732
+ privateKey = privateKeyObject
2733
+ .export({
2734
+ format: "pem",
2735
+ type: "pkcs8",
2736
+ })
2737
+ .toString();
2738
+ }
2739
+ else {
2740
+ privateKey = parts.certificateContents;
2741
+ }
2742
+ this.msalConfig.auth.clientCertificate = {
2743
+ thumbprint: parts.thumbprint,
2744
+ privateKey: privateKey,
2745
+ x5c: parts.x5c,
2746
+ };
2747
+ }
2748
+ catch (error) {
2749
+ this.logger.info(formatError("", error));
2750
+ throw error;
2751
+ }
2752
+ return super.init(options);
2767
2753
  }
2768
- if (!process.env.IDENTITY_HEADER) {
2769
- throw new Error("Missing environment variable: IDENTITY_HEADER");
2754
+ async doGetToken(scopes, options = {}) {
2755
+ try {
2756
+ const clientCredReq = {
2757
+ scopes,
2758
+ correlationId: options.correlationId,
2759
+ azureRegion: this.azureRegion,
2760
+ authority: options.authority,
2761
+ claims: options.claims,
2762
+ };
2763
+ const result = await this.confidentialApp.acquireTokenByClientCredential(clientCredReq);
2764
+ // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential,
2765
+ // The Client Credential flow does not return the account information from the authentication service,
2766
+ // so each time getToken gets called, we will have to acquire a new token through the service.
2767
+ return this.handleResult(scopes, this.clientId, result || undefined);
2768
+ }
2769
+ catch (err) {
2770
+ throw this.handleError(scopes, err, options);
2771
+ }
2770
2772
  }
2771
- return {
2772
- url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
2773
- method: "GET",
2774
- headers: coreRestPipeline.createHttpHeaders({
2775
- Accept: "application/json",
2776
- secret: process.env.IDENTITY_HEADER,
2777
- }),
2778
- };
2779
2773
  }
2774
+
2775
+ // Copyright (c) Microsoft Corporation.
2776
+ const credentialName$2 = "ClientCertificateCredential";
2777
+ const logger$8 = credentialLogger(credentialName$2);
2780
2778
  /**
2781
- * Defines how to determine whether the Azure Service Fabric MSI is available, and also how to retrieve a token from the Azure Service Fabric MSI.
2779
+ * Enables authentication to Azure Active Directory using a PEM-encoded
2780
+ * certificate that is assigned to an App Registration. More information
2781
+ * on how to configure certificate authentication can be found here:
2782
+ *
2783
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad
2784
+ *
2782
2785
  */
2783
- const fabricMsi = {
2784
- async isAvailable({ scopes }) {
2785
- const resource = mapScopesToResource(scopes);
2786
- if (!resource) {
2787
- logger$7.info(`${msiName$1}: Unavailable. Multiple scopes are not supported.`);
2788
- return false;
2786
+ class ClientCertificateCredential {
2787
+ constructor(tenantId, clientId, certificatePathOrConfiguration, options = {}) {
2788
+ if (!tenantId || !clientId) {
2789
+ throw new Error(`${credentialName$2}: tenantId and clientId are required parameters.`);
2789
2790
  }
2790
- const env = process.env;
2791
- const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER && env.IDENTITY_SERVER_THUMBPRINT);
2792
- if (!result) {
2793
- logger$7.info(`${msiName$1}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT, IDENTITY_HEADER and IDENTITY_SERVER_THUMBPRINT`);
2791
+ this.tenantId = tenantId;
2792
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
2793
+ const configuration = Object.assign({}, (typeof certificatePathOrConfiguration === "string"
2794
+ ? {
2795
+ certificatePath: certificatePathOrConfiguration,
2796
+ }
2797
+ : certificatePathOrConfiguration));
2798
+ const certificate = configuration
2799
+ .certificate;
2800
+ const certificatePath = configuration.certificatePath;
2801
+ if (!configuration || !(certificate || certificatePath)) {
2802
+ throw new Error(`${credentialName$2}: Provide either a PEM certificate in string form, or the path to that certificate in the filesystem. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
2794
2803
  }
2795
- return result;
2796
- },
2797
- async getToken(configuration, getTokenOptions = {}) {
2798
- const { scopes, identityClient, clientId, resourceId } = configuration;
2799
- if (resourceId) {
2800
- logger$7.warning(`${msiName$1}: user defined managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
2804
+ if (certificate && certificatePath) {
2805
+ throw new Error(`${credentialName$2}: To avoid unexpected behaviors, providing both the contents of a PEM certificate and the path to a PEM certificate is forbidden. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
2801
2806
  }
2802
- logger$7.info([
2803
- `${msiName$1}:`,
2804
- "Using the endpoint and the secret coming from the environment variables:",
2805
- `IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT},`,
2806
- "IDENTITY_HEADER=[REDACTED] and",
2807
- "IDENTITY_SERVER_THUMBPRINT=[REDACTED].",
2808
- ].join(" "));
2809
- const request = coreRestPipeline.createPipelineRequest(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$1(scopes, clientId, resourceId)));
2810
- request.agent = new https__default["default"].Agent({
2811
- // This is necessary because Service Fabric provides a self-signed certificate.
2812
- // The alternative path is to verify the certificate using the IDENTITY_SERVER_THUMBPRINT env variable.
2813
- rejectUnauthorized: false,
2807
+ this.msalFlow = new MsalClientCertificate(Object.assign(Object.assign({}, options), { configuration,
2808
+ logger: logger$8,
2809
+ clientId,
2810
+ tenantId, sendCertificateChain: options.sendCertificateChain, tokenCredentialOptions: options }));
2811
+ }
2812
+ /**
2813
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2814
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2815
+ *
2816
+ * @param scopes - The list of scopes for which the token will have access.
2817
+ * @param options - The options used to configure any requests this
2818
+ * TokenCredential implementation might make.
2819
+ */
2820
+ async getToken(scopes, options = {}) {
2821
+ return tracingClient.withSpan(`${credentialName$2}.getToken`, options, async (newOptions) => {
2822
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
2823
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2824
+ return this.msalFlow.getToken(arrayScopes, newOptions);
2814
2825
  });
2815
- const tokenResponse = await identityClient.sendTokenRequest(request, expiresOnParser$1);
2816
- return (tokenResponse && tokenResponse.accessToken) || null;
2817
- },
2818
- };
2826
+ }
2827
+ }
2819
2828
 
2820
2829
  // Copyright (c) Microsoft Corporation.
2821
- const msiName = "ManagedIdentityCredential - AppServiceMSI 2019";
2822
- const logger$6 = credentialLogger(msiName);
2823
2830
  /**
2824
- * Formats the expiration date of the received token into the number of milliseconds between that date and midnight, January 1, 1970.
2831
+ * MSAL client secret client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
2832
+ * @internal
2825
2833
  */
2826
- function expiresOnParser(requestBody) {
2827
- // App Service always returns string expires_on values.
2828
- return Date.parse(requestBody.expires_on);
2834
+ class MsalClientSecret extends MsalNode {
2835
+ constructor(options) {
2836
+ super(options);
2837
+ this.requiresConfidential = true;
2838
+ this.msalConfig.auth.clientSecret = options.clientSecret;
2839
+ }
2840
+ async doGetToken(scopes, options = {}) {
2841
+ try {
2842
+ const result = await this.confidentialApp.acquireTokenByClientCredential({
2843
+ scopes,
2844
+ correlationId: options.correlationId,
2845
+ azureRegion: this.azureRegion,
2846
+ authority: options.authority,
2847
+ claims: options.claims,
2848
+ });
2849
+ // The Client Credential flow does not return an account,
2850
+ // so each time getToken gets called, we will have to acquire a new token through the service.
2851
+ return this.handleResult(scopes, this.clientId, result || undefined);
2852
+ }
2853
+ catch (err) {
2854
+ throw this.handleError(scopes, err, options);
2855
+ }
2856
+ }
2829
2857
  }
2858
+
2859
+ // Copyright (c) Microsoft Corporation.
2860
+ const logger$7 = credentialLogger("ClientSecretCredential");
2830
2861
  /**
2831
- * Generates the options used on the request for an access token.
2862
+ * Enables authentication to Azure Active Directory using a client secret
2863
+ * that was generated for an App Registration. More information on how
2864
+ * to configure a client secret can be found here:
2865
+ *
2866
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application
2867
+ *
2832
2868
  */
2833
- function prepareRequestOptions(scopes, clientId, resourceId) {
2834
- const resource = mapScopesToResource(scopes);
2835
- if (!resource) {
2836
- throw new Error(`${msiName}: Multiple scopes are not supported.`);
2837
- }
2838
- const queryParameters = {
2839
- resource,
2840
- "api-version": "2019-08-01",
2841
- };
2842
- if (clientId) {
2843
- queryParameters.client_id = clientId;
2869
+ class ClientSecretCredential {
2870
+ /**
2871
+ * Creates an instance of the ClientSecretCredential with the details
2872
+ * needed to authenticate against Azure Active Directory with a client
2873
+ * secret.
2874
+ *
2875
+ * @param tenantId - The Azure Active Directory tenant (directory) ID.
2876
+ * @param clientId - The client (application) ID of an App Registration in the tenant.
2877
+ * @param clientSecret - A client secret that was generated for the App Registration.
2878
+ * @param options - Options for configuring the client which makes the authentication request.
2879
+ */
2880
+ constructor(tenantId, clientId, clientSecret, options = {}) {
2881
+ if (!tenantId || !clientId || !clientSecret) {
2882
+ throw new Error("ClientSecretCredential: tenantId, clientId, and clientSecret are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
2883
+ }
2884
+ this.tenantId = tenantId;
2885
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
2886
+ this.msalFlow = new MsalClientSecret(Object.assign(Object.assign({}, options), { logger: logger$7,
2887
+ clientId,
2888
+ tenantId,
2889
+ clientSecret, tokenCredentialOptions: options }));
2844
2890
  }
2845
- if (resourceId) {
2846
- queryParameters.mi_res_id = resourceId;
2891
+ /**
2892
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2893
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2894
+ *
2895
+ * @param scopes - The list of scopes for which the token will have access.
2896
+ * @param options - The options used to configure any requests this
2897
+ * TokenCredential implementation might make.
2898
+ */
2899
+ async getToken(scopes, options = {}) {
2900
+ return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
2901
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
2902
+ const arrayScopes = ensureScopes(scopes);
2903
+ return this.msalFlow.getToken(arrayScopes, newOptions);
2904
+ });
2847
2905
  }
2848
- const query = new URLSearchParams(queryParameters);
2849
- // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2850
- if (!process.env.IDENTITY_ENDPOINT) {
2851
- throw new Error(`${msiName}: Missing environment variable: IDENTITY_ENDPOINT`);
2906
+ }
2907
+
2908
+ // Copyright (c) Microsoft Corporation.
2909
+ /**
2910
+ * MSAL username and password client. Calls to the MSAL's public application's `acquireTokenByUsernamePassword` during `doGetToken`.
2911
+ * @internal
2912
+ */
2913
+ class MsalUsernamePassword extends MsalNode {
2914
+ constructor(options) {
2915
+ super(options);
2916
+ this.username = options.username;
2917
+ this.password = options.password;
2852
2918
  }
2853
- if (!process.env.IDENTITY_HEADER) {
2854
- throw new Error(`${msiName}: Missing environment variable: IDENTITY_HEADER`);
2919
+ async doGetToken(scopes, options) {
2920
+ try {
2921
+ const requestOptions = {
2922
+ scopes,
2923
+ username: this.username,
2924
+ password: this.password,
2925
+ correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
2926
+ authority: options === null || options === void 0 ? void 0 : options.authority,
2927
+ claims: options === null || options === void 0 ? void 0 : options.claims,
2928
+ };
2929
+ const result = await this.publicApp.acquireTokenByUsernamePassword(requestOptions);
2930
+ return this.handleResult(scopes, this.clientId, result || undefined);
2931
+ }
2932
+ catch (error) {
2933
+ throw this.handleError(scopes, error, options);
2934
+ }
2855
2935
  }
2856
- return {
2857
- url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
2858
- method: "GET",
2859
- headers: coreRestPipeline.createHttpHeaders({
2860
- Accept: "application/json",
2861
- "X-IDENTITY-HEADER": process.env.IDENTITY_HEADER,
2862
- }),
2863
- };
2864
2936
  }
2937
+
2938
+ // Copyright (c) Microsoft Corporation.
2939
+ const logger$6 = credentialLogger("UsernamePasswordCredential");
2865
2940
  /**
2866
- * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
2941
+ * Enables authentication to Azure Active Directory with a user's
2942
+ * username and password. This credential requires a high degree of
2943
+ * trust so you should only use it when other, more secure credential
2944
+ * types can't be used.
2867
2945
  */
2868
- const appServiceMsi2019 = {
2869
- async isAvailable({ scopes }) {
2870
- const resource = mapScopesToResource(scopes);
2871
- if (!resource) {
2872
- logger$6.info(`${msiName}: Unavailable. Multiple scopes are not supported.`);
2873
- return false;
2874
- }
2875
- const env = process.env;
2876
- const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER);
2877
- if (!result) {
2878
- logger$6.info(`${msiName}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT and IDENTITY_HEADER.`);
2946
+ class UsernamePasswordCredential {
2947
+ /**
2948
+ * Creates an instance of the UsernamePasswordCredential with the details
2949
+ * needed to authenticate against Azure Active Directory with a username
2950
+ * and password.
2951
+ *
2952
+ * @param tenantId - The Azure Active Directory tenant (directory).
2953
+ * @param clientId - The client (application) ID of an App Registration in the tenant.
2954
+ * @param username - The user account's e-mail address (user name).
2955
+ * @param password - The user account's account password
2956
+ * @param options - Options for configuring the client which makes the authentication request.
2957
+ */
2958
+ constructor(tenantId, clientId, username, password, options = {}) {
2959
+ if (!tenantId || !clientId || !username || !password) {
2960
+ throw new Error("UsernamePasswordCredential: tenantId, clientId, username and password are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
2879
2961
  }
2880
- return result;
2881
- },
2882
- async getToken(configuration, getTokenOptions = {}) {
2883
- const { identityClient, scopes, clientId, resourceId } = configuration;
2884
- logger$6.info(`${msiName}: Using the endpoint and the secret coming form the environment variables: IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT} and IDENTITY_HEADER=[REDACTED].`);
2885
- const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions(scopes, clientId, resourceId)), {
2886
- // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
2887
- allowInsecureConnection: true }));
2888
- const tokenResponse = await identityClient.sendTokenRequest(request, expiresOnParser);
2889
- return (tokenResponse && tokenResponse.accessToken) || null;
2890
- },
2891
- };
2962
+ this.tenantId = tenantId;
2963
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
2964
+ this.msalFlow = new MsalUsernamePassword(Object.assign(Object.assign({}, options), { logger: logger$6,
2965
+ clientId,
2966
+ tenantId,
2967
+ username,
2968
+ password, tokenCredentialOptions: options || {} }));
2969
+ }
2970
+ /**
2971
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2972
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2973
+ *
2974
+ * If the user provided the option `disableAutomaticAuthentication`,
2975
+ * once the token can't be retrieved silently,
2976
+ * this method won't attempt to request user interaction to retrieve the token.
2977
+ *
2978
+ * @param scopes - The list of scopes for which the token will have access.
2979
+ * @param options - The options used to configure any requests this
2980
+ * TokenCredential implementation might make.
2981
+ */
2982
+ async getToken(scopes, options = {}) {
2983
+ return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
2984
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
2985
+ const arrayScopes = ensureScopes(scopes);
2986
+ return this.msalFlow.getToken(arrayScopes, newOptions);
2987
+ });
2988
+ }
2989
+ }
2892
2990
 
2893
2991
  // Copyright (c) Microsoft Corporation.
2894
- const logger$5 = credentialLogger("ManagedIdentityCredential");
2895
2992
  /**
2896
- * Attempts authentication using a managed identity available at the deployment environment.
2897
- * This authentication type works in Azure VMs, App Service instances, Azure Functions applications,
2898
- * Azure Kubernetes Services, Azure Service Fabric instances and inside of the Azure Cloud Shell.
2993
+ * Contains the list of all supported environment variable names so that an
2994
+ * appropriate error message can be generated when no credentials can be
2995
+ * configured.
2899
2996
  *
2900
- * More information about configuring managed identities can be found here:
2901
- * https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
2997
+ * @internal
2902
2998
  */
2903
- class ManagedIdentityCredential {
2999
+ const AllSupportedEnvironmentVariables = [
3000
+ "AZURE_TENANT_ID",
3001
+ "AZURE_CLIENT_ID",
3002
+ "AZURE_CLIENT_SECRET",
3003
+ "AZURE_CLIENT_CERTIFICATE_PATH",
3004
+ "AZURE_CLIENT_CERTIFICATE_PASSWORD",
3005
+ "AZURE_USERNAME",
3006
+ "AZURE_PASSWORD",
3007
+ "AZURE_ADDITIONALLY_ALLOWED_TENANTS",
3008
+ ];
3009
+ function getAdditionallyAllowedTenants() {
3010
+ var _a;
3011
+ const additionallyAllowedValues = (_a = process.env.AZURE_ADDITIONALLY_ALLOWED_TENANTS) !== null && _a !== void 0 ? _a : "";
3012
+ return additionallyAllowedValues.split(";");
3013
+ }
3014
+ const credentialName$1 = "EnvironmentCredential";
3015
+ const logger$5 = credentialLogger(credentialName$1);
3016
+ /**
3017
+ * Enables authentication to Azure Active Directory using client secret
3018
+ * details configured in environment variables
3019
+ */
3020
+ class EnvironmentCredential {
2904
3021
  /**
2905
- * @internal
2906
- * @hidden
3022
+ * Creates an instance of the EnvironmentCredential class and decides what credential to use depending on the available environment variables.
3023
+ *
3024
+ * Required environment variables:
3025
+ * - `AZURE_TENANT_ID`: The Azure Active Directory tenant (directory) ID.
3026
+ * - `AZURE_CLIENT_ID`: The client (application) ID of an App Registration in the tenant.
3027
+ *
3028
+ * If setting the AZURE_TENANT_ID, then you can also set the additionally allowed tenants
3029
+ * - `AZURE_ADDITIONALLY_ALLOWED_TENANTS`: For multi-tenant applications, specifies additional tenants for which the credential may acquire tokens with a single semicolon delimited string. Use * to allow all tenants.
3030
+ *
3031
+ * Environment variables used for client credential authentication:
3032
+ * - `AZURE_CLIENT_SECRET`: A client secret that was generated for the App Registration.
3033
+ * - `AZURE_CLIENT_CERTIFICATE_PATH`: The path to a PEM certificate to use during the authentication, instead of the client secret.
3034
+ * - `AZURE_CLIENT_CERTIFICATE_PASSWORD`: (optional) password for the certificate file.
3035
+ *
3036
+ * Alternatively, users can provide environment variables for username and password authentication:
3037
+ * - `AZURE_USERNAME`: Username to authenticate with.
3038
+ * - `AZURE_PASSWORD`: Password to authenticate with.
3039
+ *
3040
+ * If the environment variables required to perform the authentication are missing, a {@link CredentialUnavailableError} will be thrown.
3041
+ * If the authentication fails, or if there's an unknown error, an {@link AuthenticationError} will be thrown.
3042
+ *
3043
+ * @param options - Options for configuring the client which makes the authentication request.
2907
3044
  */
2908
- constructor(clientIdOrOptions, options) {
2909
- this.isEndpointUnavailable = null;
2910
- let _options;
2911
- if (typeof clientIdOrOptions === "string") {
2912
- this.clientId = clientIdOrOptions;
2913
- _options = options;
2914
- }
2915
- else {
2916
- this.clientId = clientIdOrOptions === null || clientIdOrOptions === void 0 ? void 0 : clientIdOrOptions.clientId;
2917
- _options = clientIdOrOptions;
2918
- }
2919
- this.resourceId = _options === null || _options === void 0 ? void 0 : _options.resourceId;
2920
- // For JavaScript users.
2921
- if (this.clientId && this.resourceId) {
2922
- throw new Error(`${ManagedIdentityCredential.name} - Client Id and Resource Id can't be provided at the same time.`);
2923
- }
2924
- this.identityClient = new IdentityClient(_options);
2925
- this.isAvailableIdentityClient = new IdentityClient(Object.assign(Object.assign({}, _options), { retryOptions: {
2926
- maxRetries: 0,
2927
- } }));
2928
- }
2929
- async cachedAvailableMSI(scopes, getTokenOptions) {
2930
- if (this.cachedMSI) {
2931
- return this.cachedMSI;
2932
- }
2933
- const MSIs = [
2934
- arcMsi,
2935
- fabricMsi,
2936
- appServiceMsi2019,
2937
- appServiceMsi2017,
2938
- cloudShellMsi,
2939
- tokenExchangeMsi(),
2940
- imdsMsi,
2941
- ];
2942
- for (const msi of MSIs) {
2943
- if (await msi.isAvailable({
2944
- scopes,
2945
- identityClient: this.isAvailableIdentityClient,
2946
- clientId: this.clientId,
2947
- resourceId: this.resourceId,
2948
- getTokenOptions,
2949
- })) {
2950
- this.cachedMSI = msi;
2951
- return msi;
2952
- }
3045
+ constructor(options) {
3046
+ // Keep track of any missing environment variables for error details
3047
+ this._credential = undefined;
3048
+ const assigned = processEnvVars(AllSupportedEnvironmentVariables).assigned.join(", ");
3049
+ logger$5.info(`Found the following environment variables: ${assigned}`);
3050
+ const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
3051
+ const additionallyAllowedTenantIds = getAdditionallyAllowedTenants();
3052
+ const newOptions = Object.assign(Object.assign({}, options), { additionallyAllowedTenantIds });
3053
+ if (tenantId) {
3054
+ checkTenantId(logger$5, tenantId);
2953
3055
  }
2954
- throw new CredentialUnavailableError(`${ManagedIdentityCredential.name} - No MSI credential available`);
2955
- }
2956
- async authenticateManagedIdentity(scopes, getTokenOptions) {
2957
- const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.authenticateManagedIdentity`, getTokenOptions);
2958
- try {
2959
- // Determining the available MSI, and avoiding checking for other MSIs while the program is running.
2960
- const availableMSI = await this.cachedAvailableMSI(scopes, updatedOptions);
2961
- return availableMSI.getToken({
2962
- identityClient: this.identityClient,
2963
- scopes,
2964
- clientId: this.clientId,
2965
- resourceId: this.resourceId,
2966
- }, updatedOptions);
3056
+ if (tenantId && clientId && clientSecret) {
3057
+ logger$5.info(`Invoking ClientSecretCredential with tenant ID: ${tenantId}, clientId: ${clientId} and clientSecret: [REDACTED]`);
3058
+ this._credential = new ClientSecretCredential(tenantId, clientId, clientSecret, newOptions);
3059
+ return;
2967
3060
  }
2968
- catch (err) {
2969
- span.setStatus({
2970
- status: "error",
2971
- error: err,
2972
- });
2973
- throw err;
3061
+ const certificatePath = process.env.AZURE_CLIENT_CERTIFICATE_PATH;
3062
+ const certificatePassword = process.env.AZURE_CLIENT_CERTIFICATE_PASSWORD;
3063
+ if (tenantId && clientId && certificatePath) {
3064
+ logger$5.info(`Invoking ClientCertificateCredential with tenant ID: ${tenantId}, clientId: ${clientId} and certificatePath: ${certificatePath}`);
3065
+ this._credential = new ClientCertificateCredential(tenantId, clientId, { certificatePath, certificatePassword }, newOptions);
3066
+ return;
2974
3067
  }
2975
- finally {
2976
- span.end();
3068
+ const username = process.env.AZURE_USERNAME;
3069
+ const password = process.env.AZURE_PASSWORD;
3070
+ if (tenantId && clientId && username && password) {
3071
+ logger$5.info(`Invoking UsernamePasswordCredential with tenant ID: ${tenantId}, clientId: ${clientId} and username: ${username}`);
3072
+ this._credential = new UsernamePasswordCredential(tenantId, clientId, username, password, newOptions);
2977
3073
  }
2978
3074
  }
2979
3075
  /**
2980
3076
  * Authenticates with Azure Active Directory and returns an access token if successful.
2981
- * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2982
- * If an unexpected error occurs, an {@link AuthenticationError} will be thrown with the details of the failure.
2983
3077
  *
2984
3078
  * @param scopes - The list of scopes for which the token will have access.
2985
- * @param options - The options used to configure any requests this
2986
- * TokenCredential implementation might make.
3079
+ * @param options - Optional parameters. See {@link GetTokenOptions}.
2987
3080
  */
2988
- async getToken(scopes, options) {
2989
- let result = null;
2990
- const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.getToken`, options);
2991
- try {
2992
- // isEndpointAvailable can be true, false, or null,
2993
- // If it's null, it means we don't yet know whether
2994
- // the endpoint is available and need to check for it.
2995
- if (this.isEndpointUnavailable !== true) {
2996
- result = await this.authenticateManagedIdentity(scopes, updatedOptions);
2997
- if (result === null) {
2998
- // If authenticateManagedIdentity returns null,
2999
- // it means no MSI endpoints are available.
3000
- // If so, we avoid trying to reach to them in future requests.
3001
- this.isEndpointUnavailable = true;
3002
- // It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method),
3003
- // yet we had no access token. For this reason, we'll throw once with a specific message:
3004
- const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
3005
- logger$5.getToken.info(formatError(scopes, error));
3006
- throw error;
3081
+ async getToken(scopes, options = {}) {
3082
+ return tracingClient.withSpan(`${credentialName$1}.getToken`, options, async (newOptions) => {
3083
+ if (this._credential) {
3084
+ try {
3085
+ const result = await this._credential.getToken(scopes, newOptions);
3086
+ logger$5.getToken.info(formatSuccess(scopes));
3087
+ return result;
3088
+ }
3089
+ catch (err) {
3090
+ const authenticationError = new AuthenticationError(400, {
3091
+ error: `${credentialName$1} authentication failed. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.`,
3092
+ error_description: err.message.toString().split("More details:").join(""),
3093
+ });
3094
+ logger$5.getToken.info(formatError(scopes, authenticationError));
3095
+ throw authenticationError;
3007
3096
  }
3008
- // Since `authenticateManagedIdentity` didn't throw, and the result was not null,
3009
- // We will assume that this endpoint is reachable from this point forward,
3010
- // and avoid pinging again to it.
3011
- this.isEndpointUnavailable = false;
3012
- }
3013
- else {
3014
- // We've previously determined that the endpoint was unavailable,
3015
- // either because it was unreachable or permanently unable to authenticate.
3016
- const error = new CredentialUnavailableError("The managed identity endpoint is not currently available");
3017
- logger$5.getToken.info(formatError(scopes, error));
3018
- throw error;
3019
- }
3020
- logger$5.getToken.info(formatSuccess(scopes));
3021
- return result;
3022
- }
3023
- catch (err) {
3024
- // CredentialUnavailable errors are expected to reach here.
3025
- // We intend them to bubble up, so that DefaultAzureCredential can catch them.
3026
- if (err.name === "AuthenticationRequiredError") {
3027
- throw err;
3028
- }
3029
- // Expected errors to reach this point:
3030
- // - Errors coming from a method unexpectedly breaking.
3031
- // - When identityClient.sendTokenRequest throws, in which case
3032
- // if the status code was 400, it means that the endpoint is working,
3033
- // but no identity is available.
3034
- span.setStatus({
3035
- status: "error",
3036
- error: err,
3037
- });
3038
- // If either the network is unreachable,
3039
- // we can safely assume the credential is unavailable.
3040
- if (err.code === "ENETUNREACH") {
3041
- const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. Network unreachable. Message: ${err.message}`);
3042
- logger$5.getToken.info(formatError(scopes, error));
3043
- throw error;
3044
- }
3045
- // If either the host was unreachable,
3046
- // we can safely assume the credential is unavailable.
3047
- if (err.code === "EHOSTUNREACH") {
3048
- const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. No managed identity endpoint found. Message: ${err.message}`);
3049
- logger$5.getToken.info(formatError(scopes, error));
3050
- throw error;
3051
- }
3052
- // If err.statusCode has a value of 400, it comes from sendTokenRequest,
3053
- // and it means that the endpoint is working, but that no identity is available.
3054
- if (err.statusCode === 400) {
3055
- throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`);
3056
- }
3057
- // If the error has no status code, we can assume there was no available identity.
3058
- // This will throw silently during any ChainedTokenCredential.
3059
- if (err.statusCode === undefined) {
3060
- throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Authentication failed. Message ${err.message}`);
3061
3097
  }
3062
- // Any other error should break the chain.
3063
- throw new AuthenticationError(err.statusCode, {
3064
- error: `${ManagedIdentityCredential.name} authentication failed.`,
3065
- error_description: err.message,
3066
- });
3067
- }
3068
- finally {
3069
- // Finally is always called, both if we return and if we throw in the above try/catch.
3070
- span.end();
3071
- }
3098
+ throw new CredentialUnavailableError(`${credentialName$1} is unavailable. No underlying credential could be used. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.`);
3099
+ });
3072
3100
  }
3073
3101
  }
3074
3102
 
@@ -3178,6 +3206,7 @@ class ClientAssertionCredential {
3178
3206
  throw new Error("ClientAssertionCredential: tenantId, clientId, and clientAssertion are required parameters.");
3179
3207
  }
3180
3208
  this.tenantId = tenantId;
3209
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
3181
3210
  this.clientId = clientId;
3182
3211
  this.options = options;
3183
3212
  this.msalFlow = new MsalClientAssertion(Object.assign(Object.assign({}, options), { logger: logger$4, clientId: this.clientId, tenantId: this.tenantId, tokenCredentialOptions: this.options, getAssertion }));
@@ -3192,6 +3221,7 @@ class ClientAssertionCredential {
3192
3221
  */
3193
3222
  async getToken(scopes, options = {}) {
3194
3223
  return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3224
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
3195
3225
  const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3196
3226
  return this.msalFlow.getToken(arrayScopes, newOptions);
3197
3227
  });
@@ -3381,6 +3411,8 @@ class InteractiveBrowserCredential {
3381
3411
  const redirectUri = typeof options.redirectUri === "function"
3382
3412
  ? options.redirectUri()
3383
3413
  : options.redirectUri || "http://localhost";
3414
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
3415
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
3384
3416
  this.msalFlow = new MsalOpenBrowser(Object.assign(Object.assign({}, options), { tokenCredentialOptions: options, logger: logger$3,
3385
3417
  redirectUri }));
3386
3418
  this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
@@ -3399,7 +3431,8 @@ class InteractiveBrowserCredential {
3399
3431
  */
3400
3432
  async getToken(scopes, options = {}) {
3401
3433
  return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3402
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3434
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
3435
+ const arrayScopes = ensureScopes(scopes);
3403
3436
  return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
3404
3437
  });
3405
3438
  }
@@ -3418,7 +3451,7 @@ class InteractiveBrowserCredential {
3418
3451
  */
3419
3452
  async authenticate(scopes, options = {}) {
3420
3453
  return tracingClient.withSpan(`${this.constructor.name}.authenticate`, options, async (newOptions) => {
3421
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3454
+ const arrayScopes = ensureScopes(scopes);
3422
3455
  await this.msalFlow.getToken(arrayScopes, newOptions);
3423
3456
  return this.msalFlow.getActiveAccount();
3424
3457
  });
@@ -3492,6 +3525,8 @@ class DeviceCodeCredential {
3492
3525
  * @param options - Options for configuring the client which makes the authentication requests.
3493
3526
  */
3494
3527
  constructor(options) {
3528
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
3529
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
3495
3530
  this.msalFlow = new MsalDeviceCode(Object.assign(Object.assign({}, options), { logger: logger$2, userPromptCallback: (options === null || options === void 0 ? void 0 : options.userPromptCallback) || defaultDeviceCodePromptCallback, tokenCredentialOptions: options || {} }));
3496
3531
  this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
3497
3532
  }
@@ -3509,7 +3544,8 @@ class DeviceCodeCredential {
3509
3544
  */
3510
3545
  async getToken(scopes, options = {}) {
3511
3546
  return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3512
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3547
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
3548
+ const arrayScopes = ensureScopes(scopes);
3513
3549
  return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
3514
3550
  });
3515
3551
  }
@@ -3603,6 +3639,9 @@ class AuthorizationCodeCredential {
3603
3639
  clientSecret = undefined;
3604
3640
  options = redirectUriOrOptions;
3605
3641
  }
3642
+ // TODO: Validate tenant if provided
3643
+ this.tenantId = tenantId;
3644
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
3606
3645
  this.msalFlow = new MsalAuthorizationCode(Object.assign(Object.assign({}, options), { clientSecret,
3607
3646
  clientId,
3608
3647
  tenantId, tokenCredentialOptions: options || {}, logger: logger$1, redirectUri: this.redirectUri, authorizationCode: this.authorizationCode }));
@@ -3617,7 +3656,9 @@ class AuthorizationCodeCredential {
3617
3656
  */
3618
3657
  async getToken(scopes, options = {}) {
3619
3658
  return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3620
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3659
+ const tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
3660
+ newOptions.tenantId = tenantId;
3661
+ const arrayScopes = ensureScopes(scopes);
3621
3662
  return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
3622
3663
  });
3623
3664
  }
@@ -3687,10 +3728,12 @@ class OnBehalfOfCredential {
3687
3728
  this.options = options;
3688
3729
  const { clientSecret } = options;
3689
3730
  const { certificatePath } = options;
3690
- const { tenantId, clientId, userAssertionToken } = options;
3731
+ const { tenantId, clientId, userAssertionToken, additionallyAllowedTenants: additionallyAllowedTenantIds, } = options;
3691
3732
  if (!tenantId || !clientId || !(clientSecret || certificatePath) || !userAssertionToken) {
3692
3733
  throw new Error(`${credentialName}: tenantId, clientId, clientSecret (or certificatePath) and userAssertionToken are required parameters.`);
3693
3734
  }
3735
+ this.tenantId = tenantId;
3736
+ this.additionallyAllowedTenantIds = resolveAddionallyAllowedTenantIds(additionallyAllowedTenantIds);
3694
3737
  this.msalFlow = new MsalOnBehalfOf(Object.assign(Object.assign({}, this.options), { logger, tokenCredentialOptions: this.options }));
3695
3738
  }
3696
3739
  /**
@@ -3702,7 +3745,8 @@ class OnBehalfOfCredential {
3702
3745
  */
3703
3746
  async getToken(scopes, options = {}) {
3704
3747
  return tracingClient.withSpan(`${credentialName}.getToken`, options, async (newOptions) => {
3705
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3748
+ newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds);
3749
+ const arrayScopes = ensureScopes(scopes);
3706
3750
  return this.msalFlow.getToken(arrayScopes, newOptions);
3707
3751
  });
3708
3752
  }