@azure/identity 2.0.0-beta.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (149) hide show
  1. package/CHANGELOG.md +232 -6
  2. package/README.md +124 -39
  3. package/dist/index.js +2317 -1596
  4. package/dist/index.js.map +1 -1
  5. package/dist-esm/src/client/identityClient.js +147 -133
  6. package/dist-esm/src/client/identityClient.js.map +1 -1
  7. package/dist-esm/src/constants.js +1 -1
  8. package/dist-esm/src/constants.js.map +1 -1
  9. package/dist-esm/src/credentials/authorizationCodeCredential.browser.js +1 -1
  10. package/dist-esm/src/credentials/authorizationCodeCredential.browser.js.map +1 -1
  11. package/dist-esm/src/credentials/authorizationCodeCredential.js +13 -76
  12. package/dist-esm/src/credentials/authorizationCodeCredential.js.map +1 -1
  13. package/dist-esm/src/credentials/azureApplicationCredential.browser.js +34 -0
  14. package/dist-esm/src/credentials/azureApplicationCredential.browser.js.map +1 -0
  15. package/dist-esm/src/credentials/azureApplicationCredential.js +36 -0
  16. package/dist-esm/src/credentials/azureApplicationCredential.js.map +1 -0
  17. package/dist-esm/src/credentials/azureCliCredential.browser.js +7 -0
  18. package/dist-esm/src/credentials/azureCliCredential.browser.js.map +1 -1
  19. package/dist-esm/src/credentials/azureCliCredential.js +110 -83
  20. package/dist-esm/src/credentials/azureCliCredential.js.map +1 -1
  21. package/dist-esm/src/credentials/azureCliCredentialOptions.js +4 -0
  22. package/dist-esm/src/credentials/azureCliCredentialOptions.js.map +1 -0
  23. package/dist-esm/src/credentials/azurePowerShellCredential.browser.js +3 -1
  24. package/dist-esm/src/credentials/azurePowerShellCredential.browser.js.map +1 -1
  25. package/dist-esm/src/credentials/azurePowerShellCredential.js +93 -83
  26. package/dist-esm/src/credentials/azurePowerShellCredential.js.map +1 -1
  27. package/dist-esm/src/credentials/azurePowerShellCredentialOptions.js +4 -0
  28. package/dist-esm/src/credentials/azurePowerShellCredentialOptions.js.map +1 -0
  29. package/dist-esm/src/credentials/chainedTokenCredential.js +34 -37
  30. package/dist-esm/src/credentials/chainedTokenCredential.js.map +1 -1
  31. package/dist-esm/src/credentials/clientCertificateCredential.browser.js +7 -0
  32. package/dist-esm/src/credentials/clientCertificateCredential.browser.js.map +1 -1
  33. package/dist-esm/src/credentials/clientCertificateCredential.js +24 -23
  34. package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
  35. package/dist-esm/src/credentials/clientCertificateCredentialOptions.js.map +1 -1
  36. package/dist-esm/src/credentials/clientSecretCredential.browser.js +39 -44
  37. package/dist-esm/src/credentials/clientSecretCredential.browser.js.map +1 -1
  38. package/dist-esm/src/credentials/clientSecretCredential.js +9 -11
  39. package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
  40. package/dist-esm/src/credentials/clientSecretCredentialOptions.js.map +1 -1
  41. package/dist-esm/src/credentials/credentialPersistenceOptions.js +4 -0
  42. package/dist-esm/src/credentials/credentialPersistenceOptions.js.map +1 -0
  43. package/dist-esm/src/credentials/defaultAzureCredential.browser.js +1 -1
  44. package/dist-esm/src/credentials/defaultAzureCredential.browser.js.map +1 -1
  45. package/dist-esm/src/credentials/defaultAzureCredential.js +50 -27
  46. package/dist-esm/src/credentials/defaultAzureCredential.js.map +1 -1
  47. package/dist-esm/src/credentials/deviceCodeCredential.browser.js +7 -0
  48. package/dist-esm/src/credentials/deviceCodeCredential.browser.js.map +1 -1
  49. package/dist-esm/src/credentials/deviceCodeCredential.js +27 -22
  50. package/dist-esm/src/credentials/deviceCodeCredential.js.map +1 -1
  51. package/dist-esm/src/credentials/deviceCodeCredentialOptions.js.map +1 -1
  52. package/dist-esm/src/credentials/environmentCredential.browser.js +7 -0
  53. package/dist-esm/src/credentials/environmentCredential.browser.js.map +1 -1
  54. package/dist-esm/src/credentials/environmentCredential.js +39 -38
  55. package/dist-esm/src/credentials/environmentCredential.js.map +1 -1
  56. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js +20 -29
  57. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js.map +1 -1
  58. package/dist-esm/src/credentials/interactiveBrowserCredential.js +23 -29
  59. package/dist-esm/src/credentials/interactiveBrowserCredential.js.map +1 -1
  60. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js.map +1 -1
  61. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js +36 -22
  62. package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js.map +1 -1
  63. package/dist-esm/src/credentials/managedIdentityCredential/arcMsi.js +62 -47
  64. package/dist-esm/src/credentials/managedIdentityCredential/arcMsi.js.map +1 -1
  65. package/dist-esm/src/credentials/managedIdentityCredential/cloudShellMsi.js +33 -22
  66. package/dist-esm/src/credentials/managedIdentityCredential/cloudShellMsi.js.map +1 -1
  67. package/dist-esm/src/credentials/managedIdentityCredential/constants.js +2 -1
  68. package/dist-esm/src/credentials/managedIdentityCredential/constants.js.map +1 -1
  69. package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js +42 -27
  70. package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js.map +1 -1
  71. package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js +115 -91
  72. package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js.map +1 -1
  73. package/dist-esm/src/credentials/managedIdentityCredential/index.browser.js +3 -6
  74. package/dist-esm/src/credentials/managedIdentityCredential/index.browser.js.map +1 -1
  75. package/dist-esm/src/credentials/managedIdentityCredential/index.js +120 -125
  76. package/dist-esm/src/credentials/managedIdentityCredential/index.js.map +1 -1
  77. package/dist-esm/src/credentials/managedIdentityCredential/models.js.map +1 -1
  78. package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js +82 -0
  79. package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js.map +1 -0
  80. package/dist-esm/src/credentials/managedIdentityCredential/utils.js +14 -8
  81. package/dist-esm/src/credentials/managedIdentityCredential/utils.js.map +1 -1
  82. package/dist-esm/src/credentials/onBehalfOfCredential.browser.js +23 -0
  83. package/dist-esm/src/credentials/onBehalfOfCredential.browser.js.map +1 -0
  84. package/dist-esm/src/credentials/onBehalfOfCredential.js +57 -0
  85. package/dist-esm/src/credentials/onBehalfOfCredential.js.map +1 -0
  86. package/dist-esm/src/credentials/onBehalfOfCredentialOptions.js +4 -0
  87. package/dist-esm/src/credentials/onBehalfOfCredentialOptions.js.map +1 -0
  88. package/dist-esm/src/credentials/usernamePasswordCredential.browser.js +41 -46
  89. package/dist-esm/src/credentials/usernamePasswordCredential.browser.js.map +1 -1
  90. package/dist-esm/src/credentials/usernamePasswordCredential.js +9 -13
  91. package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -1
  92. package/dist-esm/src/credentials/usernamePasswordCredentialOptions.js.map +1 -1
  93. package/dist-esm/src/credentials/visualStudioCodeCredential.browser.js +27 -0
  94. package/dist-esm/src/credentials/visualStudioCodeCredential.browser.js.map +1 -0
  95. package/dist-esm/src/credentials/visualStudioCodeCredential.js +183 -0
  96. package/dist-esm/src/credentials/visualStudioCodeCredential.js.map +1 -0
  97. package/dist-esm/src/credentials/visualStudioCodeCredentialPlugin.js +4 -0
  98. package/dist-esm/src/credentials/visualStudioCodeCredentialPlugin.js.map +1 -0
  99. package/dist-esm/src/{client/errors.js → errors.js} +16 -1
  100. package/dist-esm/src/errors.js.map +1 -0
  101. package/dist-esm/src/index.js +4 -2
  102. package/dist-esm/src/index.js.map +1 -1
  103. package/dist-esm/src/msal/browserFlows/browserCommon.js +33 -31
  104. package/dist-esm/src/msal/browserFlows/browserCommon.js.map +1 -1
  105. package/dist-esm/src/msal/browserFlows/msalAuthCode.js +113 -115
  106. package/dist-esm/src/msal/browserFlows/msalAuthCode.js.map +1 -1
  107. package/dist-esm/src/msal/credentials.js.map +1 -1
  108. package/dist-esm/src/msal/flows.js.map +1 -1
  109. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js +41 -0
  110. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js.map +1 -0
  111. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js +65 -46
  112. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js.map +1 -1
  113. package/dist-esm/src/msal/nodeFlows/msalClientSecret.js +15 -16
  114. package/dist-esm/src/msal/nodeFlows/msalClientSecret.js.map +1 -1
  115. package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js +20 -22
  116. package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js.map +1 -1
  117. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js +56 -0
  118. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js.map +1 -0
  119. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js +44 -33
  120. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js.map +1 -1
  121. package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js +15 -17
  122. package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js.map +1 -1
  123. package/dist-esm/src/msal/nodeFlows/nodeCommon.js +141 -98
  124. package/dist-esm/src/msal/nodeFlows/nodeCommon.js.map +1 -1
  125. package/dist-esm/src/msal/nodeFlows/tokenCachePersistenceOptions.js +4 -0
  126. package/dist-esm/src/msal/nodeFlows/tokenCachePersistenceOptions.js.map +1 -0
  127. package/dist-esm/src/msal/utils.js +23 -15
  128. package/dist-esm/src/msal/utils.js.map +1 -1
  129. package/dist-esm/src/plugins/consumer.browser.js +7 -0
  130. package/dist-esm/src/plugins/consumer.browser.js.map +1 -0
  131. package/dist-esm/src/plugins/consumer.js +44 -0
  132. package/dist-esm/src/plugins/consumer.js.map +1 -0
  133. package/dist-esm/src/plugins/provider.js +4 -0
  134. package/dist-esm/src/plugins/provider.js.map +1 -0
  135. package/dist-esm/src/regionalAuthority.js +115 -0
  136. package/dist-esm/src/regionalAuthority.js.map +1 -0
  137. package/dist-esm/src/util/tracing.js +24 -27
  138. package/dist-esm/src/util/tracing.js.map +1 -1
  139. package/dist-esm/src/util/validateMultiTenant.browser.js +22 -0
  140. package/dist-esm/src/util/validateMultiTenant.browser.js.map +1 -0
  141. package/dist-esm/src/util/validateMultiTenant.js +29 -0
  142. package/dist-esm/src/util/validateMultiTenant.js.map +1 -0
  143. package/package.json +44 -28
  144. package/types/identity.d.ts +482 -126
  145. package/dist-esm/src/client/errors.js.map +0 -1
  146. package/dist-esm/src/msal/errors.js +0 -22
  147. package/dist-esm/src/msal/errors.js.map +0 -1
  148. package/dist-esm/src/util/authHostEnv.js +0 -13
  149. package/dist-esm/src/util/authHostEnv.js.map +0 -1
package/dist/index.js CHANGED
@@ -4,22 +4,65 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
6
 
7
- var tslib = require('tslib');
8
- var coreTracing = require('@azure/core-tracing');
9
- var logger$g = require('@azure/logger');
10
7
  var msalNode = require('@azure/msal-node');
11
- var qs = _interopDefault(require('qs'));
12
- var coreHttp = require('@azure/core-http');
8
+ var coreClient = require('@azure/core-client');
9
+ var coreTracing = require('@azure/core-tracing');
10
+ var coreUtil = require('@azure/core-util');
11
+ var coreRestPipeline = require('@azure/core-rest-pipeline');
13
12
  var abortController = require('@azure/abort-controller');
13
+ var logger$j = require('@azure/logger');
14
14
  var msalCommon = require('@azure/msal-common');
15
15
  var uuid = require('uuid');
16
16
  var fs = require('fs');
17
- var crypto = require('crypto');
17
+ var fs__default = _interopDefault(fs);
18
+ var os = _interopDefault(require('os'));
19
+ var path = _interopDefault(require('path'));
18
20
  var child_process = require('child_process');
21
+ var child_process__default = _interopDefault(child_process);
22
+ var crypto = require('crypto');
23
+ var util = require('util');
19
24
  var http = _interopDefault(require('http'));
20
25
  var open = _interopDefault(require('open'));
21
26
  var stoppable = _interopDefault(require('stoppable'));
22
27
 
28
+ // Copyright (c) Microsoft Corporation.
29
+ // Licensed under the MIT license.
30
+ /**
31
+ * The default client ID for authentication
32
+ * @internal
33
+ */
34
+ // TODO: temporary - this is the Azure CLI clientID - we'll replace it when
35
+ // Developer Sign On application is available
36
+ // https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/Constants.cs#L9
37
+ const DeveloperSignOnClientId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
38
+ /**
39
+ * The default tenant for authentication
40
+ * @internal
41
+ */
42
+ const DefaultTenantId = "common";
43
+ (function (AzureAuthorityHosts) {
44
+ /**
45
+ * China-based Azure Authority Host
46
+ */
47
+ AzureAuthorityHosts["AzureChina"] = "https://login.chinacloudapi.cn";
48
+ /**
49
+ * Germany-based Azure Authority Host
50
+ */
51
+ AzureAuthorityHosts["AzureGermany"] = "https://login.microsoftonline.de";
52
+ /**
53
+ * US Government Azure Authority Host
54
+ */
55
+ AzureAuthorityHosts["AzureGovernment"] = "https://login.microsoftonline.us";
56
+ /**
57
+ * Public Cloud Azure Authority Host
58
+ */
59
+ AzureAuthorityHosts["AzurePublicCloud"] = "https://login.microsoftonline.com";
60
+ })(exports.AzureAuthorityHosts || (exports.AzureAuthorityHosts = {}));
61
+ /**
62
+ * The default authority host.
63
+ */
64
+ const DefaultAuthorityHost = exports.AzureAuthorityHosts.AzurePublicCloud;
65
+
23
66
  // Copyright (c) Microsoft Corporation.
24
67
  // Licensed under the MIT license.
25
68
  function isErrorResponse(errorResponse) {
@@ -107,7 +150,7 @@ const AggregateAuthenticationErrorName = "AggregateAuthenticationError";
107
150
  class AggregateAuthenticationError extends Error {
108
151
  constructor(errors, errorMessage) {
109
152
  const errorDetail = errors.join("\n");
110
- super(`${errorMessage}\n\n${errorDetail}`);
153
+ super(`${errorMessage}\n${errorDetail}`);
111
154
  this.errors = errors;
112
155
  // Ensure that this type reports the correct name
113
156
  this.name = AggregateAuthenticationErrorName;
@@ -123,6 +166,32 @@ function convertOAuthErrorResponseToErrorResponse(errorBody) {
123
166
  traceId: errorBody.trace_id
124
167
  };
125
168
  }
169
+ /**
170
+ * Error used to enforce authentication after trying to retrieve a token silently.
171
+ */
172
+ class AuthenticationRequiredError extends Error {
173
+ constructor(
174
+ /**
175
+ * Optional parameters. A message can be specified. The {@link GetTokenOptions} of the request can also be specified to more easily associate the error with the received parameters.
176
+ */
177
+ options) {
178
+ super(options.message);
179
+ this.scopes = options.scopes;
180
+ this.getTokenOptions = options.getTokenOptions;
181
+ this.name = "AuthenticationRequiredError";
182
+ }
183
+ }
184
+
185
+ // Copyright (c) Microsoft Corporation.
186
+ // Licensed under the MIT license.
187
+ function getIdentityTokenEndpointSuffix(tenantId) {
188
+ if (tenantId === "adfs") {
189
+ return "oauth2/token";
190
+ }
191
+ else {
192
+ return "oauth2/v2.0/token";
193
+ }
194
+ }
126
195
 
127
196
  // Copyright (c) Microsoft Corporation.
128
197
  /**
@@ -130,7 +199,7 @@ function convertOAuthErrorResponseToErrorResponse(errorBody) {
130
199
  * @internal
131
200
  */
132
201
  const createSpan = coreTracing.createSpanFunction({
133
- packagePrefix: "Azure.Identity",
202
+ packagePrefix: "",
134
203
  namespace: "Microsoft.AAD"
135
204
  });
136
205
  /**
@@ -138,42 +207,40 @@ const createSpan = coreTracing.createSpanFunction({
138
207
  * Traces an operation and properly handles reporting start, end and errors for a given span
139
208
  *
140
209
  * @param operationName - Name of a method in the TClient type
141
- * @param options - An options class, typically derived from \@azure/core-http/RequestOptionsBase
210
+ * @param options - An options class, typically derived from \@azure/core-rest-pipeline/RequestOptionsBase
142
211
  * @param fn - The function to call with an options class that properly propagates the span context
143
212
  *
144
213
  * @internal
145
214
  */
146
- function trace(operationName, options, fn, createSpanFn = createSpan) {
147
- return tslib.__awaiter(this, void 0, void 0, function* () {
148
- const { updatedOptions, span } = createSpanFn(operationName, options);
149
- try {
150
- // NOTE: we really do need to await on this function here so we can handle any exceptions thrown and properly
151
- // close the span.
152
- const result = yield fn(updatedOptions, span);
153
- // otel 0.16+ needs this or else the code ends up being set as UNSET
154
- span.setStatus({
155
- code: coreTracing.SpanStatusCode.OK
156
- });
157
- return result;
158
- }
159
- catch (err) {
160
- span.setStatus({
161
- code: coreTracing.SpanStatusCode.ERROR,
162
- message: err.message
163
- });
164
- throw err;
165
- }
166
- finally {
167
- span.end();
168
- }
169
- });
215
+ async function trace(operationName, options, fn, createSpanFn = createSpan) {
216
+ const { updatedOptions, span } = createSpanFn(operationName, options);
217
+ try {
218
+ // NOTE: we really do need to await on this function here so we can handle any exceptions thrown and properly
219
+ // close the span.
220
+ const result = await fn(updatedOptions, span);
221
+ // otel 0.16+ needs this or else the code ends up being set as UNSET
222
+ span.setStatus({
223
+ code: coreTracing.SpanStatusCode.OK
224
+ });
225
+ return result;
226
+ }
227
+ catch (err) {
228
+ span.setStatus({
229
+ code: coreTracing.SpanStatusCode.ERROR,
230
+ message: err.message
231
+ });
232
+ throw err;
233
+ }
234
+ finally {
235
+ span.end();
236
+ }
170
237
  }
171
238
 
172
239
  // Copyright (c) Microsoft Corporation.
173
240
  /**
174
241
  * The AzureLogger used for all clients within the identity package
175
242
  */
176
- const logger = logger$g.createClientLogger("identity");
243
+ const logger = logger$j.createClientLogger("identity");
177
244
  /**
178
245
  * Separates a list of environment variable names into a plain object with two arrays: an array of missing environment variables and another array with assigned environment variables.
179
246
  * @param supportedEnvVars - List of environment variable names
@@ -240,339 +307,208 @@ function credentialLogger(title, log = logger) {
240
307
  }
241
308
 
242
309
  // Copyright (c) Microsoft Corporation.
310
+ const noCorrelationId = "noCorrelationId";
243
311
  /**
244
312
  * @internal
245
313
  */
246
- const logger$1 = credentialLogger("ChainedTokenCredential");
314
+ function getIdentityClientAuthorityHost(options) {
315
+ // The authorityHost can come from options or from the AZURE_AUTHORITY_HOST environment variable.
316
+ let authorityHost = options === null || options === void 0 ? void 0 : options.authorityHost;
317
+ // The AZURE_AUTHORITY_HOST environment variable can only be provided in Node.js.
318
+ if (coreUtil.isNode) {
319
+ authorityHost = authorityHost !== null && authorityHost !== void 0 ? authorityHost : process.env.AZURE_AUTHORITY_HOST;
320
+ }
321
+ // If the authorityHost is not provided, we use the default one from the public cloud: https://login.microsoftonline.com
322
+ return authorityHost !== null && authorityHost !== void 0 ? authorityHost : DefaultAuthorityHost;
323
+ }
247
324
  /**
248
- * Enables multiple `TokenCredential` implementations to be tried in order
249
- * until one of the getToken methods returns an access token.
325
+ * The network module used by the Identity credentials.
326
+ *
327
+ * It allows for credentials to abort any pending request independently of the MSAL flow,
328
+ * by calling to the `abortRequests()` method.
329
+ *
250
330
  */
251
- class ChainedTokenCredential {
252
- /**
253
- * Creates an instance of ChainedTokenCredential using the given credentials.
254
- *
255
- * @param sources - `TokenCredential` implementations to be tried in order.
256
- *
257
- * Example usage:
258
- * ```javascript
259
- * const firstCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
260
- * const secondCredential = new ClientSecretCredential(tenantId, anotherClientId, anotherSecret);
261
- * const credentialChain = new ChainedTokenCredential(firstCredential, secondCredential);
262
- * ```
263
- */
264
- constructor(...sources) {
265
- /**
266
- * The message to use when the chained token fails to get a token
267
- */
268
- this.UnavailableMessage = "ChainedTokenCredential => failed to retrieve a token from the included credentials";
269
- this._sources = [];
270
- this._sources = sources;
331
+ class IdentityClient extends coreClient.ServiceClient {
332
+ constructor(options) {
333
+ var _a;
334
+ const packageDetails = `azsdk-js-identity/2.0.0`;
335
+ const userAgentPrefix = ((_a = options === null || options === void 0 ? void 0 : options.userAgentOptions) === null || _a === void 0 ? void 0 : _a.userAgentPrefix)
336
+ ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}`
337
+ : `${packageDetails}`;
338
+ const baseUri = getIdentityClientAuthorityHost(options);
339
+ if (!baseUri.startsWith("https:")) {
340
+ throw new Error("The authorityHost address must use the 'https' protocol.");
341
+ }
342
+ super(Object.assign(Object.assign({ requestContentType: "application/json; charset=utf-8" }, options), { userAgentOptions: {
343
+ userAgentPrefix
344
+ }, baseUri }));
345
+ this.authorityHost = baseUri;
346
+ this.abortControllers = new Map();
271
347
  }
272
- /**
273
- * Returns the first access token returned by one of the chained
274
- * `TokenCredential` implementations. Throws an {@link AggregateAuthenticationError}
275
- * when one or more credentials throws an {@link AuthenticationError} and
276
- * no credentials have returned an access token.
277
- *
278
- * This method is called automatically by Azure SDK client libraries. You may call this method
279
- * directly, but you must also handle token caching and token refreshing.
280
- *
281
- * @param scopes - The list of scopes for which the token will have access.
282
- * @param options - The options used to configure any requests this
283
- * `TokenCredential` implementation might make.
284
- */
285
- getToken(scopes, options) {
286
- return tslib.__awaiter(this, void 0, void 0, function* () {
287
- let token = null;
288
- let successfulCredentialName = "";
289
- const errors = [];
290
- const { span, updatedOptions } = createSpan("ChainedTokenCredential-getToken", options);
291
- for (let i = 0; i < this._sources.length && token === null; i++) {
292
- try {
293
- token = yield this._sources[i].getToken(scopes, updatedOptions);
294
- successfulCredentialName = this._sources[i].constructor.name;
295
- }
296
- catch (err) {
297
- if (err.name === "CredentialUnavailableError" ||
298
- err.name === "AuthenticationRequiredError") {
299
- errors.push(err);
300
- }
301
- else {
302
- logger$1.getToken.info(formatError(scopes, err));
303
- throw err;
304
- }
305
- }
348
+ async sendTokenRequest(request, expiresOnParser) {
349
+ logger.info(`IdentityClient: sending token request to [${request.url}]`);
350
+ const response = await this.sendRequest(request);
351
+ expiresOnParser =
352
+ expiresOnParser ||
353
+ ((responseBody) => {
354
+ return Date.now() + responseBody.expires_in * 1000;
355
+ });
356
+ if (response.bodyAsText && (response.status === 200 || response.status === 201)) {
357
+ const parsedBody = JSON.parse(response.bodyAsText);
358
+ if (!parsedBody.access_token) {
359
+ return null;
360
+ }
361
+ const token = {
362
+ accessToken: {
363
+ token: parsedBody.access_token,
364
+ expiresOnTimestamp: expiresOnParser(parsedBody)
365
+ },
366
+ refreshToken: parsedBody.refresh_token
367
+ };
368
+ logger.info(`IdentityClient: [${request.url}] token acquired, expires on ${token.accessToken.expiresOnTimestamp}`);
369
+ return token;
370
+ }
371
+ else {
372
+ const error = new AuthenticationError(response.status, response.bodyAsText);
373
+ logger.warning(`IdentityClient: authentication error. HTTP status: ${response.status}, ${error.errorResponse.errorDescription}`);
374
+ throw error;
375
+ }
376
+ }
377
+ async refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, expiresOnParser, options) {
378
+ if (refreshToken === undefined) {
379
+ return null;
380
+ }
381
+ logger.info(`IdentityClient: refreshing access token with client ID: ${clientId}, scopes: ${scopes} started`);
382
+ const { span, updatedOptions } = createSpan("IdentityClient-refreshAccessToken", options);
383
+ const refreshParams = {
384
+ grant_type: "refresh_token",
385
+ client_id: clientId,
386
+ refresh_token: refreshToken,
387
+ scope: scopes
388
+ };
389
+ if (clientSecret !== undefined) {
390
+ refreshParams.client_secret = clientSecret;
391
+ }
392
+ const query = new URLSearchParams(refreshParams);
393
+ try {
394
+ const urlSuffix = getIdentityTokenEndpointSuffix(tenantId);
395
+ const request = coreRestPipeline.createPipelineRequest({
396
+ url: `${this.authorityHost}/${tenantId}/${urlSuffix}`,
397
+ method: "POST",
398
+ body: query.toString(),
399
+ abortSignal: options && options.abortSignal,
400
+ headers: coreRestPipeline.createHttpHeaders({
401
+ Accept: "application/json",
402
+ "Content-Type": "application/x-www-form-urlencoded"
403
+ }),
404
+ tracingOptions: updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions
405
+ });
406
+ const response = await this.sendTokenRequest(request, expiresOnParser);
407
+ logger.info(`IdentityClient: refreshed token for client ID: ${clientId}`);
408
+ return response;
409
+ }
410
+ catch (err) {
411
+ if (err.name === AuthenticationErrorName &&
412
+ err.errorResponse.error === "interaction_required") {
413
+ // It's likely that the refresh token has expired, so
414
+ // return null so that the credential implementation will
415
+ // initiate the authentication flow again.
416
+ logger.info(`IdentityClient: interaction required for client ID: ${clientId}`);
417
+ span.setStatus({
418
+ code: coreTracing.SpanStatusCode.ERROR,
419
+ message: err.message
420
+ });
421
+ return null;
306
422
  }
307
- if (!token && errors.length > 0) {
308
- const err = new AggregateAuthenticationError(errors);
423
+ else {
424
+ logger.warning(`IdentityClient: failed refreshing token for client ID: ${clientId}: ${err}`);
309
425
  span.setStatus({
310
426
  code: coreTracing.SpanStatusCode.ERROR,
311
427
  message: err.message
312
428
  });
313
- logger$1.getToken.info(formatError(scopes, err));
314
429
  throw err;
315
430
  }
431
+ }
432
+ finally {
316
433
  span.end();
317
- logger$1.getToken.info(`Result for ${successfulCredentialName}: ${formatSuccess(scopes)}`);
318
- if (token === null) {
319
- throw new CredentialUnavailableError("Failed to retrieve a valid token");
434
+ }
435
+ }
436
+ // Here is a custom layer that allows us to abort requests that go through MSAL,
437
+ // since MSAL doesn't allow us to pass options all the way through.
438
+ generateAbortSignal(correlationId) {
439
+ const controller = new abortController.AbortController();
440
+ const controllers = this.abortControllers.get(correlationId) || [];
441
+ controllers.push(controller);
442
+ this.abortControllers.set(correlationId, controllers);
443
+ const existingOnAbort = controller.signal.onabort;
444
+ controller.signal.onabort = (...params) => {
445
+ this.abortControllers.set(correlationId, undefined);
446
+ if (existingOnAbort) {
447
+ existingOnAbort(...params);
320
448
  }
321
- return token;
322
- });
449
+ };
450
+ return controller.signal;
323
451
  }
324
- }
325
-
326
- // Copyright (c) Microsoft Corporation.
327
- // Licensed under the MIT license.
328
- /**
329
- * The default client ID for authentication
330
- * @internal
331
- */
332
- // TODO: temporary - this is the Azure CLI clientID - we'll replace it when
333
- // Developer Sign On application is available
334
- // https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/src/Constants.cs#L9
335
- const DeveloperSignOnClientId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
336
- /**
337
- * The default tenant for authentication
338
- * @internal
339
- */
340
- const DefaultTenantId = "common";
341
- (function (AzureAuthorityHosts) {
342
- /**
343
- * China-based Azure Authority Host
344
- */
345
- AzureAuthorityHosts["AzureChina"] = "https://login.chinacloudapi.cn";
346
- /**
347
- * Germany-based Azure Authority Host
348
- */
349
- AzureAuthorityHosts["AzureGermany"] = "https://login.microsoftonline.de";
350
- /**
351
- * US Government Azure Authority Host
352
- */
353
- AzureAuthorityHosts["AzureGovernment"] = "https://login.microsoftonline.us";
354
- /**
355
- * Public Cloud Azure Authority Host
356
- */
357
- AzureAuthorityHosts["AzurePublicCloud"] = "https://login.microsoftonline.com";
358
- })(exports.AzureAuthorityHosts || (exports.AzureAuthorityHosts = {}));
359
- /**
360
- * The default authority host.
361
- */
362
- const DefaultAuthorityHost = exports.AzureAuthorityHosts.AzurePublicCloud;
363
-
364
- // Copyright (c) Microsoft Corporation.
365
- // Licensed under the MIT license.
366
- function getAuthorityHostEnvironment() {
367
- if (process.env.AZURE_AUTHORITY_HOST) {
452
+ abortRequests(correlationId) {
453
+ const key = correlationId || noCorrelationId;
454
+ const controllers = [
455
+ ...(this.abortControllers.get(key) || []),
456
+ // MSAL passes no correlation ID to the get requests...
457
+ ...(this.abortControllers.get(noCorrelationId) || [])
458
+ ];
459
+ if (!controllers.length) {
460
+ return;
461
+ }
462
+ for (const controller of controllers) {
463
+ controller.abort();
464
+ }
465
+ this.abortControllers.set(key, undefined);
466
+ }
467
+ getCorrelationId(options) {
468
+ var _a;
469
+ const parameter = (_a = options === null || options === void 0 ? void 0 : options.body) === null || _a === void 0 ? void 0 : _a.split("&").map((part) => part.split("=")).find(([key]) => key === "client-request-id");
470
+ return parameter && parameter.length ? parameter[1] || noCorrelationId : noCorrelationId;
471
+ }
472
+ // The MSAL network module methods follow
473
+ async sendGetRequestAsync(url, options) {
474
+ const request = coreRestPipeline.createPipelineRequest({
475
+ url,
476
+ method: "GET",
477
+ body: options === null || options === void 0 ? void 0 : options.body,
478
+ headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers),
479
+ abortSignal: this.generateAbortSignal(noCorrelationId)
480
+ });
481
+ const response = await this.sendRequest(request);
368
482
  return {
369
- authorityHost: process.env.AZURE_AUTHORITY_HOST
483
+ body: response.bodyAsText ? JSON.parse(response.bodyAsText) : undefined,
484
+ headers: response.headers.toJSON(),
485
+ status: response.status
370
486
  };
371
487
  }
372
- else {
373
- return undefined;
488
+ async sendPostRequestAsync(url, options) {
489
+ const request = coreRestPipeline.createPipelineRequest({
490
+ url,
491
+ method: "POST",
492
+ body: options === null || options === void 0 ? void 0 : options.body,
493
+ headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers),
494
+ // MSAL doesn't send the correlation ID on the get requests.
495
+ abortSignal: this.generateAbortSignal(this.getCorrelationId(options))
496
+ });
497
+ const response = await this.sendRequest(request);
498
+ return {
499
+ body: response.bodyAsText ? JSON.parse(response.bodyAsText) : undefined,
500
+ headers: response.headers.toJSON(),
501
+ status: response.status
502
+ };
374
503
  }
375
504
  }
376
505
 
377
506
  // Copyright (c) Microsoft Corporation.
378
- // Licensed under the MIT license.
379
- function getIdentityTokenEndpointSuffix(tenantId) {
380
- if (tenantId === "adfs") {
381
- return "oauth2/token";
382
- }
383
- else {
384
- return "oauth2/v2.0/token";
385
- }
386
- }
387
-
388
- // Copyright (c) Microsoft Corporation.
389
- const noCorrelationId = "noCorrelationId";
390
- /**
391
- * The network module used by the Identity credentials.
392
- *
393
- * It allows for credentials to abort any pending request independently of the MSAL flow,
394
- * by calling to the `abortRequests()` method.
395
- *
396
- * @internal
397
- */
398
- class IdentityClient extends coreHttp.ServiceClient {
399
- constructor(options) {
400
- if (coreHttp.isNode) {
401
- options = options || getAuthorityHostEnvironment();
402
- }
403
- // Only if the authorityHost is not provided, we use the default one.
404
- options = Object.assign({ authorityHost: DefaultAuthorityHost }, options);
405
- super(undefined, coreHttp.createPipelineFromOptions(Object.assign(Object.assign({}, options), { deserializationOptions: {
406
- expectedContentTypes: {
407
- json: ["application/json", "text/json", "text/plain"]
408
- }
409
- } })));
410
- this.baseUri = this.authorityHost = options.authorityHost || DefaultAuthorityHost;
411
- if (!this.baseUri.startsWith("https:")) {
412
- throw new Error("The authorityHost address must use the 'https' protocol.");
413
- }
414
- this.abortControllers = new Map();
415
- }
416
- createWebResource(requestOptions) {
417
- const webResource = new coreHttp.WebResource();
418
- webResource.prepare(requestOptions);
419
- return webResource;
420
- }
421
- sendTokenRequest(webResource, expiresOnParser) {
422
- return tslib.__awaiter(this, void 0, void 0, function* () {
423
- logger.info(`IdentityClient: sending token request to [${webResource.url}]`);
424
- const response = yield this.sendRequest(webResource);
425
- expiresOnParser =
426
- expiresOnParser ||
427
- ((responseBody) => {
428
- return Date.now() + responseBody.expires_in * 1000;
429
- });
430
- if (response.status === 200 || response.status === 201) {
431
- const token = {
432
- accessToken: {
433
- token: response.parsedBody.access_token,
434
- expiresOnTimestamp: expiresOnParser(response.parsedBody)
435
- },
436
- refreshToken: response.parsedBody.refresh_token
437
- };
438
- logger.info(`IdentityClient: [${webResource.url}] token acquired, expires on ${token.accessToken.expiresOnTimestamp}`);
439
- return token;
440
- }
441
- else {
442
- const error = new AuthenticationError(response.status, response.parsedBody || response.bodyAsText);
443
- logger.warning(`IdentityClient: authentication error. HTTP status: ${response.status}, ${error.errorResponse.errorDescription}`);
444
- throw error;
445
- }
446
- });
447
- }
448
- refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, expiresOnParser, options) {
449
- var _a, _b;
450
- return tslib.__awaiter(this, void 0, void 0, function* () {
451
- if (refreshToken === undefined) {
452
- return null;
453
- }
454
- logger.info(`IdentityClient: refreshing access token with client ID: ${clientId}, scopes: ${scopes} started`);
455
- const { span, updatedOptions } = createSpan("IdentityClient-refreshAccessToken", options);
456
- const refreshParams = {
457
- grant_type: "refresh_token",
458
- client_id: clientId,
459
- refresh_token: refreshToken,
460
- scope: scopes
461
- };
462
- if (clientSecret !== undefined) {
463
- refreshParams.client_secret = clientSecret;
464
- }
465
- try {
466
- const urlSuffix = getIdentityTokenEndpointSuffix(tenantId);
467
- const webResource = this.createWebResource({
468
- url: `${this.authorityHost}/${tenantId}/${urlSuffix}`,
469
- method: "POST",
470
- disableJsonStringifyOnBody: true,
471
- deserializationMapper: undefined,
472
- body: qs.stringify(refreshParams),
473
- headers: {
474
- Accept: "application/json",
475
- "Content-Type": "application/x-www-form-urlencoded"
476
- },
477
- spanOptions: (_a = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _a === void 0 ? void 0 : _a.spanOptions,
478
- tracingContext: (_b = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _b === void 0 ? void 0 : _b.tracingContext,
479
- abortSignal: options && options.abortSignal
480
- });
481
- const response = yield this.sendTokenRequest(webResource, expiresOnParser);
482
- logger.info(`IdentityClient: refreshed token for client ID: ${clientId}`);
483
- return response;
484
- }
485
- catch (err) {
486
- if (err.name === AuthenticationErrorName &&
487
- err.errorResponse.error === "interaction_required") {
488
- // It's likely that the refresh token has expired, so
489
- // return null so that the credential implementation will
490
- // initiate the authentication flow again.
491
- logger.info(`IdentityClient: interaction required for client ID: ${clientId}`);
492
- span.setStatus({
493
- code: coreTracing.SpanStatusCode.ERROR,
494
- message: err.message
495
- });
496
- return null;
497
- }
498
- else {
499
- logger.warning(`IdentityClient: failed refreshing token for client ID: ${clientId}: ${err}`);
500
- span.setStatus({
501
- code: coreTracing.SpanStatusCode.ERROR,
502
- message: err.message
503
- });
504
- throw err;
505
- }
506
- }
507
- finally {
508
- span.end();
509
- }
510
- });
511
- }
512
- // Here is a custom layer that allows us to abort requests that go through MSAL,
513
- // since MSAL doesn't allow us to pass options all the way through.
514
- generateAbortSignal(correlationId) {
515
- const controller = new abortController.AbortController();
516
- const key = correlationId || noCorrelationId;
517
- const controllers = this.abortControllers.get(key) || [];
518
- controllers.push(controller);
519
- this.abortControllers.set(key, controllers);
520
- return controller.signal;
521
- }
522
- abortRequests(correlationId = noCorrelationId) {
523
- const key = correlationId || noCorrelationId;
524
- const controllers = [
525
- ...(this.abortControllers.get(key) || []),
526
- // MSAL passes no correlation ID to the get requests...
527
- ...(this.abortControllers.get(noCorrelationId) || [])
528
- ];
529
- if (!controllers.length) {
530
- return;
531
- }
532
- for (const controller of controllers) {
533
- controller.abort();
534
- }
535
- this.abortControllers.set(key, undefined);
536
- this.abortControllers.set(noCorrelationId, undefined);
537
- }
538
- getCorrelationId(options) {
539
- var _a;
540
- const parameter = (_a = options === null || options === void 0 ? void 0 : options.body) === null || _a === void 0 ? void 0 : _a.split("&").map((part) => part.split("=")).find(([key]) => key === "client-request-id");
541
- return parameter && parameter.length ? parameter[1] : noCorrelationId;
542
- }
543
- // The MSAL network module methods follow
544
- sendGetRequestAsync(url, options) {
545
- const webResource = new coreHttp.WebResource(url, "GET", options === null || options === void 0 ? void 0 : options.body, {}, options === null || options === void 0 ? void 0 : options.headers, false, false,
546
- // MSAL doesn't send the correlation ID on the get requests.
547
- this.generateAbortSignal());
548
- return this.sendRequest(webResource).then((response) => {
549
- return {
550
- body: response.parsedBody,
551
- headers: response.headers.rawHeaders(),
552
- status: response.status
553
- };
554
- });
555
- }
556
- sendPostRequestAsync(url, options) {
557
- const webResource = new coreHttp.WebResource(url, "POST", options === null || options === void 0 ? void 0 : options.body, {}, options === null || options === void 0 ? void 0 : options.headers, false, false,
558
- // MSAL doesn't send the correlation ID on the get requests.
559
- this.generateAbortSignal(this.getCorrelationId(options)));
560
- return this.sendRequest(webResource).then((response) => {
561
- return {
562
- body: response.parsedBody,
563
- headers: response.headers.rawHeaders(),
564
- status: response.status
565
- };
566
- });
567
- }
568
- }
569
-
570
- // Copyright (c) Microsoft Corporation.
571
- function checkTenantId(logger, tenantId) {
572
- if (!tenantId.match(/^[0-9a-zA-Z-.:/]+$/)) {
573
- 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.");
574
- logger.info(formatError("", error));
575
- throw error;
507
+ function checkTenantId(logger, tenantId) {
508
+ if (!tenantId.match(/^[0-9a-zA-Z-.:/]+$/)) {
509
+ 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.");
510
+ logger.info(formatError("", error));
511
+ throw error;
576
512
  }
577
513
  }
578
514
 
@@ -591,28 +527,6 @@ function resolveTenantId(logger, tenantId, clientId) {
591
527
  return "organizations";
592
528
  }
593
529
 
594
- // Copyright (c) Microsoft Corporation.
595
- // Licensed under the MIT license.
596
- /**
597
- * Error used to enforce authentication after trying to retrieve a token silently.
598
- */
599
- class AuthenticationRequiredError extends Error {
600
- constructor(
601
- /**
602
- * The list of scopes for which the token will have access.
603
- */
604
- scopes,
605
- /**
606
- * The options used to configure the getToken request.
607
- */
608
- getTokenOptions = {}, message) {
609
- super(message);
610
- this.scopes = scopes;
611
- this.getTokenOptions = getTokenOptions;
612
- this.name = "AuthenticationRequiredError";
613
- }
614
- }
615
-
616
530
  // Copyright (c) Microsoft Corporation.
617
531
  /**
618
532
  * Latest AuthenticationRecord version
@@ -626,7 +540,11 @@ const LatestAuthenticationRecordVersion = "1.0";
626
540
  function ensureValidMsalToken(scopes, logger, msalToken, getTokenOptions) {
627
541
  const error = (message) => {
628
542
  logger.getToken.info(message);
629
- return new AuthenticationRequiredError(Array.isArray(scopes) ? scopes : [scopes], getTokenOptions, message);
543
+ return new AuthenticationRequiredError({
544
+ scopes: Array.isArray(scopes) ? scopes : [scopes],
545
+ getTokenOptions,
546
+ message
547
+ });
630
548
  };
631
549
  if (!msalToken) {
632
550
  throw error("No response");
@@ -639,10 +557,13 @@ function ensureValidMsalToken(scopes, logger, msalToken, getTokenOptions) {
639
557
  }
640
558
  }
641
559
  /**
642
- * Generates a valid authorityHost by combining a host with a tenantId.
560
+ * Generates a valid authority by combining a host with a tenantId.
643
561
  * @internal
644
562
  */
645
- function getAuthorityHost(tenantId, host = DefaultAuthorityHost) {
563
+ function getAuthority(tenantId, host) {
564
+ if (!host) {
565
+ host = DefaultAuthorityHost;
566
+ }
646
567
  if (host.endsWith("/")) {
647
568
  return host + tenantId;
648
569
  }
@@ -652,8 +573,9 @@ function getAuthorityHost(tenantId, host = DefaultAuthorityHost) {
652
573
  }
653
574
  /**
654
575
  * Generates the known authorities.
655
- * If the tenantId is "adfs", we will return an array with the authorityHost as the only known authority.
656
- * Otherwise, it is safe to return an empty array.
576
+ * If the Tenant Id is `adfs`, the authority can't be validated since the format won't match the expected one.
577
+ * For that reason, we have to force MSAL to disable validating the authority
578
+ * by sending it within the known authorities in the MSAL configuration.
657
579
  * @internal
658
580
  */
659
581
  function getKnownAuthorities(tenantId, authorityHost) {
@@ -667,22 +589,22 @@ function getKnownAuthorities(tenantId, authorityHost) {
667
589
  * @param logger - The logger of the credential.
668
590
  * @internal
669
591
  */
670
- const defaultLoggerCallback = (logger) => (level, message, containsPii) => {
592
+ const defaultLoggerCallback = (logger, platform = coreUtil.isNode ? "Node" : "Browser") => (level, message, containsPii) => {
671
593
  if (containsPii) {
672
594
  return;
673
595
  }
674
596
  switch (level) {
675
597
  case msalCommon.LogLevel.Error:
676
- logger.info(`MSAL Browser V2 error: ${message}`);
598
+ logger.info(`MSAL ${platform} V2 error: ${message}`);
677
599
  return;
678
600
  case msalCommon.LogLevel.Info:
679
- logger.info(`MSAL Browser V2 info message: ${message}`);
601
+ logger.info(`MSAL ${platform} V2 info message: ${message}`);
680
602
  return;
681
603
  case msalCommon.LogLevel.Verbose:
682
- logger.info(`MSAL Browser V2 verbose message: ${message}`);
604
+ logger.info(`MSAL ${platform} V2 verbose message: ${message}`);
683
605
  return;
684
606
  case msalCommon.LogLevel.Warning:
685
- logger.info(`MSAL Browser V2 warning: ${message}`);
607
+ logger.info(`MSAL ${platform} V2 warning: ${message}`);
686
608
  return;
687
609
  }
688
610
  };
@@ -750,7 +672,7 @@ class MsalBaseUtilities {
750
672
  error.name === "AbortError") {
751
673
  return error;
752
674
  }
753
- return new AuthenticationRequiredError(scopes, getTokenOptions, error.message);
675
+ return new AuthenticationRequiredError({ scopes, getTokenOptions, message: error.message });
754
676
  }
755
677
  }
756
678
  // transformations.ts
@@ -760,7 +682,7 @@ function publicToMsal(account) {
760
682
  }
761
683
  function msalToPublic(clientId, account) {
762
684
  const record = {
763
- authority: getAuthorityHost(account.tenantId, account.environment),
685
+ authority: getAuthority(account.tenantId, account.environment),
764
686
  homeAccountId: account.homeAccountId,
765
687
  tenantId: account.tenantId || DefaultTenantId,
766
688
  username: account.username,
@@ -813,9 +735,167 @@ function deserializeAuthenticationRecord(serializedRecord) {
813
735
  return parsed;
814
736
  }
815
737
 
738
+ // Copyright (c) Microsoft Corporation.
739
+ // Licensed under the MIT license.
740
+ /**
741
+ * @internal
742
+ */
743
+ 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.";
744
+ /**
745
+ * @internal
746
+ */
747
+ 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`.";
748
+ /**
749
+ * Of getToken contains a tenantId, this functions allows picking this tenantId as the appropriate for authentication,
750
+ * unless multitenant authentication has been disabled through the AZURE_IDENTITY_DISABLE_MULTITENANTAUTH (on Node.js),
751
+ * or unless the original tenant Id is `adfs`.
752
+ * @internal
753
+ */
754
+ function processMultiTenantRequest(tenantId, getTokenOptions) {
755
+ if (!(getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId)) {
756
+ return tenantId;
757
+ }
758
+ if (process.env.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH) {
759
+ throw new Error(multiTenantDisabledErrorMessage);
760
+ }
761
+ if (tenantId === "adfs") {
762
+ throw new Error(multiTenantADFSErrorMessage);
763
+ }
764
+ return getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId;
765
+ }
766
+
767
+ // Copyright (c) Microsoft Corporation.
768
+ // Licensed under the MIT license.
769
+ /**
770
+ * Helps specify a regional authority, or "AutoDiscoverRegion" to auto-detect the region.
771
+ */
772
+ var RegionalAuthority;
773
+ (function (RegionalAuthority) {
774
+ /** Instructs MSAL to attempt to discover the region */
775
+ RegionalAuthority["AutoDiscoverRegion"] = "AutoDiscoverRegion";
776
+ /** Uses the {@link RegionalAuthority} for the Azure 'westus' region. */
777
+ RegionalAuthority["USWest"] = "westus";
778
+ /** Uses the {@link RegionalAuthority} for the Azure 'westus2' region. */
779
+ RegionalAuthority["USWest2"] = "westus2";
780
+ /** Uses the {@link RegionalAuthority} for the Azure 'centralus' region. */
781
+ RegionalAuthority["USCentral"] = "centralus";
782
+ /** Uses the {@link RegionalAuthority} for the Azure 'eastus' region. */
783
+ RegionalAuthority["USEast"] = "eastus";
784
+ /** Uses the {@link RegionalAuthority} for the Azure 'eastus2' region. */
785
+ RegionalAuthority["USEast2"] = "eastus2";
786
+ /** Uses the {@link RegionalAuthority} for the Azure 'northcentralus' region. */
787
+ RegionalAuthority["USNorthCentral"] = "northcentralus";
788
+ /** Uses the {@link RegionalAuthority} for the Azure 'southcentralus' region. */
789
+ RegionalAuthority["USSouthCentral"] = "southcentralus";
790
+ /** Uses the {@link RegionalAuthority} for the Azure 'westcentralus' region. */
791
+ RegionalAuthority["USWestCentral"] = "westcentralus";
792
+ /** Uses the {@link RegionalAuthority} for the Azure 'canadacentral' region. */
793
+ RegionalAuthority["CanadaCentral"] = "canadacentral";
794
+ /** Uses the {@link RegionalAuthority} for the Azure 'canadaeast' region. */
795
+ RegionalAuthority["CanadaEast"] = "canadaeast";
796
+ /** Uses the {@link RegionalAuthority} for the Azure 'brazilsouth' region. */
797
+ RegionalAuthority["BrazilSouth"] = "brazilsouth";
798
+ /** Uses the {@link RegionalAuthority} for the Azure 'northeurope' region. */
799
+ RegionalAuthority["EuropeNorth"] = "northeurope";
800
+ /** Uses the {@link RegionalAuthority} for the Azure 'westeurope' region. */
801
+ RegionalAuthority["EuropeWest"] = "westeurope";
802
+ /** Uses the {@link RegionalAuthority} for the Azure 'uksouth' region. */
803
+ RegionalAuthority["UKSouth"] = "uksouth";
804
+ /** Uses the {@link RegionalAuthority} for the Azure 'ukwest' region. */
805
+ RegionalAuthority["UKWest"] = "ukwest";
806
+ /** Uses the {@link RegionalAuthority} for the Azure 'francecentral' region. */
807
+ RegionalAuthority["FranceCentral"] = "francecentral";
808
+ /** Uses the {@link RegionalAuthority} for the Azure 'francesouth' region. */
809
+ RegionalAuthority["FranceSouth"] = "francesouth";
810
+ /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandnorth' region. */
811
+ RegionalAuthority["SwitzerlandNorth"] = "switzerlandnorth";
812
+ /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandwest' region. */
813
+ RegionalAuthority["SwitzerlandWest"] = "switzerlandwest";
814
+ /** Uses the {@link RegionalAuthority} for the Azure 'germanynorth' region. */
815
+ RegionalAuthority["GermanyNorth"] = "germanynorth";
816
+ /** Uses the {@link RegionalAuthority} for the Azure 'germanywestcentral' region. */
817
+ RegionalAuthority["GermanyWestCentral"] = "germanywestcentral";
818
+ /** Uses the {@link RegionalAuthority} for the Azure 'norwaywest' region. */
819
+ RegionalAuthority["NorwayWest"] = "norwaywest";
820
+ /** Uses the {@link RegionalAuthority} for the Azure 'norwayeast' region. */
821
+ RegionalAuthority["NorwayEast"] = "norwayeast";
822
+ /** Uses the {@link RegionalAuthority} for the Azure 'eastasia' region. */
823
+ RegionalAuthority["AsiaEast"] = "eastasia";
824
+ /** Uses the {@link RegionalAuthority} for the Azure 'southeastasia' region. */
825
+ RegionalAuthority["AsiaSouthEast"] = "southeastasia";
826
+ /** Uses the {@link RegionalAuthority} for the Azure 'japaneast' region. */
827
+ RegionalAuthority["JapanEast"] = "japaneast";
828
+ /** Uses the {@link RegionalAuthority} for the Azure 'japanwest' region. */
829
+ RegionalAuthority["JapanWest"] = "japanwest";
830
+ /** Uses the {@link RegionalAuthority} for the Azure 'australiaeast' region. */
831
+ RegionalAuthority["AustraliaEast"] = "australiaeast";
832
+ /** Uses the {@link RegionalAuthority} for the Azure 'australiasoutheast' region. */
833
+ RegionalAuthority["AustraliaSouthEast"] = "australiasoutheast";
834
+ /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral' region. */
835
+ RegionalAuthority["AustraliaCentral"] = "australiacentral";
836
+ /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral2' region. */
837
+ RegionalAuthority["AustraliaCentral2"] = "australiacentral2";
838
+ /** Uses the {@link RegionalAuthority} for the Azure 'centralindia' region. */
839
+ RegionalAuthority["IndiaCentral"] = "centralindia";
840
+ /** Uses the {@link RegionalAuthority} for the Azure 'southindia' region. */
841
+ RegionalAuthority["IndiaSouth"] = "southindia";
842
+ /** Uses the {@link RegionalAuthority} for the Azure 'westindia' region. */
843
+ RegionalAuthority["IndiaWest"] = "westindia";
844
+ /** Uses the {@link RegionalAuthority} for the Azure 'koreasouth' region. */
845
+ RegionalAuthority["KoreaSouth"] = "koreasouth";
846
+ /** Uses the {@link RegionalAuthority} for the Azure 'koreacentral' region. */
847
+ RegionalAuthority["KoreaCentral"] = "koreacentral";
848
+ /** Uses the {@link RegionalAuthority} for the Azure 'uaecentral' region. */
849
+ RegionalAuthority["UAECentral"] = "uaecentral";
850
+ /** Uses the {@link RegionalAuthority} for the Azure 'uaenorth' region. */
851
+ RegionalAuthority["UAENorth"] = "uaenorth";
852
+ /** Uses the {@link RegionalAuthority} for the Azure 'southafricanorth' region. */
853
+ RegionalAuthority["SouthAfricaNorth"] = "southafricanorth";
854
+ /** Uses the {@link RegionalAuthority} for the Azure 'southafricawest' region. */
855
+ RegionalAuthority["SouthAfricaWest"] = "southafricawest";
856
+ /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth' region. */
857
+ RegionalAuthority["ChinaNorth"] = "chinanorth";
858
+ /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast' region. */
859
+ RegionalAuthority["ChinaEast"] = "chinaeast";
860
+ /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth2' region. */
861
+ RegionalAuthority["ChinaNorth2"] = "chinanorth2";
862
+ /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast2' region. */
863
+ RegionalAuthority["ChinaEast2"] = "chinaeast2";
864
+ /** Uses the {@link RegionalAuthority} for the Azure 'germanycentral' region. */
865
+ RegionalAuthority["GermanyCentral"] = "germanycentral";
866
+ /** Uses the {@link RegionalAuthority} for the Azure 'germanynortheast' region. */
867
+ RegionalAuthority["GermanyNorthEast"] = "germanynortheast";
868
+ /** Uses the {@link RegionalAuthority} for the Azure 'usgovvirginia' region. */
869
+ RegionalAuthority["GovernmentUSVirginia"] = "usgovvirginia";
870
+ /** Uses the {@link RegionalAuthority} for the Azure 'usgoviowa' region. */
871
+ RegionalAuthority["GovernmentUSIowa"] = "usgoviowa";
872
+ /** Uses the {@link RegionalAuthority} for the Azure 'usgovarizona' region. */
873
+ RegionalAuthority["GovernmentUSArizona"] = "usgovarizona";
874
+ /** Uses the {@link RegionalAuthority} for the Azure 'usgovtexas' region. */
875
+ RegionalAuthority["GovernmentUSTexas"] = "usgovtexas";
876
+ /** Uses the {@link RegionalAuthority} for the Azure 'usdodeast' region. */
877
+ RegionalAuthority["GovernmentUSDodEast"] = "usdodeast";
878
+ /** Uses the {@link RegionalAuthority} for the Azure 'usdodcentral' region. */
879
+ RegionalAuthority["GovernmentUSDodCentral"] = "usdodcentral";
880
+ })(RegionalAuthority || (RegionalAuthority = {}));
881
+
816
882
  // Copyright (c) Microsoft Corporation.
817
883
  /**
818
- * MSAL partial base client for NodeJS.
884
+ * The current persistence provider, undefined by default.
885
+ * @internal
886
+ */
887
+ let persistenceProvider = undefined;
888
+ /**
889
+ * An object that allows setting the persistence provider.
890
+ * @internal
891
+ */
892
+ const msalNodeFlowCacheControl = {
893
+ setPersistence(pluginProvider) {
894
+ persistenceProvider = pluginProvider;
895
+ }
896
+ };
897
+ /**
898
+ * MSAL partial base client for Node.js.
819
899
  *
820
900
  * It completes the input configuration with some default values.
821
901
  * It also provides with utility protected methods that can be used from any of the clients,
@@ -825,24 +905,48 @@ function deserializeAuthenticationRecord(serializedRecord) {
825
905
  */
826
906
  class MsalNode extends MsalBaseUtilities {
827
907
  constructor(options) {
908
+ var _a, _b, _c;
828
909
  super(options);
829
910
  this.requiresConfidential = false;
830
911
  this.msalConfig = this.defaultNodeMsalConfig(options);
912
+ this.tenantId = resolveTenantId(options.logger, options.tenantId, options.clientId);
831
913
  this.clientId = this.msalConfig.auth.clientId;
914
+ // If persistence has been configured
915
+ if (persistenceProvider !== undefined && ((_a = options.tokenCachePersistenceOptions) === null || _a === void 0 ? void 0 : _a.enabled)) {
916
+ this.createCachePlugin = () => persistenceProvider(options.tokenCachePersistenceOptions);
917
+ }
918
+ else if ((_b = options.tokenCachePersistenceOptions) === null || _b === void 0 ? void 0 : _b.enabled) {
919
+ throw new Error([
920
+ "Persistent token caching was requested, but no persistence provider was configured.",
921
+ "You must install the identity-cache-persistence plugin package (`npm install --save @azure/identity-cache-persistence`)",
922
+ "and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling",
923
+ "`useIdentityPlugin(cachePersistencePlugin)` before using `tokenCachePersistenceOptions`."
924
+ ].join(" "));
925
+ }
926
+ this.azureRegion = (_c = options.regionalAuthority) !== null && _c !== void 0 ? _c : process.env.AZURE_REGIONAL_AUTHORITY_NAME;
927
+ if (this.azureRegion === RegionalAuthority.AutoDiscoverRegion) {
928
+ this.azureRegion = "AUTO_DISCOVER";
929
+ }
832
930
  }
833
931
  /**
834
- * Generates a MSAL configuration that generally works for NodeJS
932
+ * Generates a MSAL configuration that generally works for Node.js
835
933
  */
836
934
  defaultNodeMsalConfig(options) {
837
935
  const clientId = options.clientId || DeveloperSignOnClientId;
838
936
  const tenantId = resolveTenantId(options.logger, options.tenantId, options.clientId);
839
- const authorityHost = getAuthorityHost(tenantId, options.authorityHost);
840
- this.identityClient = new IdentityClient(Object.assign(Object.assign({}, options.tokenCredentialOptions), { authorityHost }));
937
+ this.authorityHost = options.authorityHost || process.env.AZURE_AUTHORITY_HOST;
938
+ const authority = getAuthority(tenantId, this.authorityHost);
939
+ this.identityClient = new IdentityClient(Object.assign(Object.assign({}, options.tokenCredentialOptions), { authorityHost: authority }));
940
+ let clientCapabilities = ["CP1"];
941
+ if (process.env.AZURE_IDENTITY_DISABLE_CP1) {
942
+ clientCapabilities = [];
943
+ }
841
944
  return {
842
945
  auth: {
843
946
  clientId,
844
- authority: authorityHost,
845
- knownAuthorities: getKnownAuthorities(tenantId, authorityHost)
947
+ authority,
948
+ knownAuthorities: getKnownAuthorities(tenantId, authority),
949
+ clientCapabilities
846
950
  },
847
951
  // Cache is defined in this.prepare();
848
952
  system: {
@@ -856,31 +960,34 @@ class MsalNode extends MsalBaseUtilities {
856
960
  /**
857
961
  * Prepares the MSAL applications.
858
962
  */
859
- init(options) {
860
- return tslib.__awaiter(this, void 0, void 0, function* () {
861
- if (options === null || options === void 0 ? void 0 : options.abortSignal) {
862
- options.abortSignal.addEventListener("abort", () => {
863
- // This will abort any pending request in the IdentityClient,
864
- // based on the received or generated correlationId
865
- this.identityClient.abortRequests(options.correlationId);
866
- });
867
- }
868
- if (this.publicApp || this.confidentialApp) {
869
- return;
870
- }
871
- this.publicApp = new msalNode.PublicClientApplication(this.msalConfig);
872
- // The confidential client requires either a secret, assertion or certificate.
873
- if (this.msalConfig.auth.clientSecret ||
874
- this.msalConfig.auth.clientAssertion ||
875
- this.msalConfig.auth.clientCertificate) {
876
- this.confidentialApp = new msalNode.ConfidentialClientApplication(this.msalConfig);
877
- }
878
- else {
879
- if (this.requiresConfidential) {
880
- throw new Error("Unable to generate the MSAL confidential client. Missing either the client's secret, certificate or assertion.");
881
- }
963
+ async init(options) {
964
+ if (options === null || options === void 0 ? void 0 : options.abortSignal) {
965
+ options.abortSignal.addEventListener("abort", () => {
966
+ // This will abort any pending request in the IdentityClient,
967
+ // based on the received or generated correlationId
968
+ this.identityClient.abortRequests(options.correlationId);
969
+ });
970
+ }
971
+ if (this.publicApp || this.confidentialApp) {
972
+ return;
973
+ }
974
+ if (this.createCachePlugin !== undefined) {
975
+ this.msalConfig.cache = {
976
+ cachePlugin: await this.createCachePlugin()
977
+ };
978
+ }
979
+ this.publicApp = new msalNode.PublicClientApplication(this.msalConfig);
980
+ // The confidential client requires either a secret, assertion or certificate.
981
+ if (this.msalConfig.auth.clientSecret ||
982
+ this.msalConfig.auth.clientAssertion ||
983
+ this.msalConfig.auth.clientCertificate) {
984
+ this.confidentialApp = new msalNode.ConfidentialClientApplication(this.msalConfig);
985
+ }
986
+ else {
987
+ if (this.requiresConfidential) {
988
+ throw new Error("Unable to generate the MSAL confidential client. Missing either the client's secret, certificate or assertion.");
882
989
  }
883
- });
990
+ }
884
991
  }
885
992
  /**
886
993
  * Allows the cancellation of a MSAL request.
@@ -902,417 +1009,1079 @@ class MsalNode extends MsalBaseUtilities {
902
1009
  /**
903
1010
  * Returns the existing account, attempts to load the account from MSAL.
904
1011
  */
905
- getActiveAccount() {
906
- var _a;
907
- return tslib.__awaiter(this, void 0, void 0, function* () {
908
- if (this.account) {
909
- return this.account;
910
- }
911
- const cache = (_a = this.publicApp) === null || _a === void 0 ? void 0 : _a.getTokenCache();
912
- const accountsByTenant = yield (cache === null || cache === void 0 ? void 0 : cache.getAllAccounts());
913
- if (!accountsByTenant) {
914
- return;
915
- }
916
- if (accountsByTenant.length === 1) {
917
- this.account = msalToPublic(this.clientId, accountsByTenant[0]);
918
- }
919
- else {
920
- this.logger
921
- .info(`More than one account was found authenticated for this Client ID and Tenant ID.
1012
+ async getActiveAccount() {
1013
+ var _a, _b, _c;
1014
+ if (this.account) {
1015
+ return this.account;
1016
+ }
1017
+ const cache = (_b = (_a = this.confidentialApp) === null || _a === void 0 ? void 0 : _a.getTokenCache()) !== null && _b !== void 0 ? _b : (_c = this.publicApp) === null || _c === void 0 ? void 0 : _c.getTokenCache();
1018
+ const accountsByTenant = await (cache === null || cache === void 0 ? void 0 : cache.getAllAccounts());
1019
+ if (!accountsByTenant) {
1020
+ return;
1021
+ }
1022
+ if (accountsByTenant.length === 1) {
1023
+ this.account = msalToPublic(this.clientId, accountsByTenant[0]);
1024
+ }
1025
+ else {
1026
+ this.logger
1027
+ .info(`More than one account was found authenticated for this Client ID and Tenant ID.
922
1028
  However, no "authenticationRecord" has been provided for this credential,
923
1029
  therefore we're unable to pick between these accounts.
924
1030
  A new login attempt will be requested, to ensure the correct account is picked.
925
1031
  To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing a credential to prevent this from happening.`);
926
- return;
927
- }
928
- return this.account;
929
- });
930
- }
931
- /**
932
- * Clears MSAL's cache.
933
- */
934
- logout() {
935
- return tslib.__awaiter(this, void 0, void 0, function* () {
936
- // Intentionally empty
937
- });
1032
+ return;
1033
+ }
1034
+ return this.account;
938
1035
  }
939
1036
  /**
940
1037
  * Attempts to retrieve a token from cache.
941
1038
  */
942
- getTokenSilent(scopes, options) {
943
- return tslib.__awaiter(this, void 0, void 0, function* () {
944
- yield this.getActiveAccount();
945
- if (!this.account) {
946
- throw new AuthenticationRequiredError(scopes, options);
947
- }
948
- const silentRequest = {
949
- // To be able to re-use the account, the Token Cache must also have been provided.
950
- account: publicToMsal(this.account),
951
- correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
952
- scopes
953
- };
954
- try {
955
- this.logger.info("Attempting to acquire token silently");
956
- const response = yield this.publicApp.acquireTokenSilent(silentRequest);
957
- return this.handleResult(scopes, this.clientId, response || undefined);
958
- }
959
- catch (err) {
960
- throw this.handleError(scopes, err, options);
961
- }
962
- });
1039
+ async getTokenSilent(scopes, options) {
1040
+ var _a, _b;
1041
+ await this.getActiveAccount();
1042
+ if (!this.account) {
1043
+ throw new AuthenticationRequiredError({
1044
+ scopes,
1045
+ getTokenOptions: options,
1046
+ message: "Silent authentication failed. We couldn't retrieve an active account from the cache."
1047
+ });
1048
+ }
1049
+ const silentRequest = {
1050
+ // To be able to re-use the account, the Token Cache must also have been provided.
1051
+ account: publicToMsal(this.account),
1052
+ correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
1053
+ scopes,
1054
+ authority: options === null || options === void 0 ? void 0 : options.authority
1055
+ };
1056
+ try {
1057
+ this.logger.info("Attempting to acquire token silently");
1058
+ const response = (_b = (await ((_a = this.confidentialApp) === null || _a === void 0 ? void 0 : _a.acquireTokenSilent(silentRequest)))) !== null && _b !== void 0 ? _b : (await this.publicApp.acquireTokenSilent(silentRequest));
1059
+ return this.handleResult(scopes, this.clientId, response || undefined);
1060
+ }
1061
+ catch (err) {
1062
+ throw this.handleError(scopes, err, options);
1063
+ }
963
1064
  }
964
1065
  /**
965
1066
  * Wrapper around each MSAL flow get token operation: doGetToken.
966
1067
  * If disableAutomaticAuthentication is sent through the constructor, it will prevent MSAL from requesting the user input.
967
1068
  */
968
- getToken(scopes, options = {}) {
969
- return tslib.__awaiter(this, void 0, void 0, function* () {
970
- options.correlationId = (options === null || options === void 0 ? void 0 : options.correlationId) || this.generateUuid();
971
- yield this.init(options);
972
- return this.getTokenSilent(scopes, options).catch((err) => {
973
- if (err.name !== "AuthenticationRequiredError") {
974
- throw err;
975
- }
976
- if (options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication) {
977
- throw new AuthenticationRequiredError(scopes, options, "Automatic authentication has been disabled. You may call the authentication() method.");
978
- }
979
- this.logger.info(`Silent authentication failed, falling back to interactive method.`);
980
- return this.doGetToken(scopes, options);
981
- });
982
- });
1069
+ async getToken(scopes, options = {}) {
1070
+ const tenantId = processMultiTenantRequest(this.tenantId, options) || this.tenantId;
1071
+ options.authority = getAuthority(tenantId, this.authorityHost);
1072
+ options.correlationId = (options === null || options === void 0 ? void 0 : options.correlationId) || this.generateUuid();
1073
+ await this.init(options);
1074
+ try {
1075
+ return await this.getTokenSilent(scopes, options);
1076
+ }
1077
+ catch (err) {
1078
+ if (err.name !== "AuthenticationRequiredError") {
1079
+ throw err;
1080
+ }
1081
+ if (options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication) {
1082
+ throw new AuthenticationRequiredError({
1083
+ scopes,
1084
+ getTokenOptions: options,
1085
+ message: "Automatic authentication has been disabled. You may call the authentication() method."
1086
+ });
1087
+ }
1088
+ this.logger.info(`Silent authentication failed, falling back to interactive method.`);
1089
+ return this.doGetToken(scopes, options);
1090
+ }
983
1091
  }
984
1092
  }
985
1093
 
986
1094
  // Copyright (c) Microsoft Corporation.
1095
+ const CommonTenantId = "common";
1096
+ const AzureAccountClientId = "aebc6443-996d-45c2-90f0-388ff96faa56"; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56'
1097
+ const logger$1 = credentialLogger("VisualStudioCodeCredential");
1098
+ let findCredentials = undefined;
1099
+ const vsCodeCredentialControl = {
1100
+ setVsCodeCredentialFinder(finder) {
1101
+ findCredentials = finder;
1102
+ }
1103
+ };
1104
+ // Map of unsupported Tenant IDs and the errors we will be throwing.
1105
+ const unsupportedTenantIds = {
1106
+ adfs: "The VisualStudioCodeCredential does not support authentication with ADFS tenants."
1107
+ };
1108
+ function checkUnsupportedTenant(tenantId) {
1109
+ // If the Tenant ID isn't supported, we throw.
1110
+ const unsupportedTenantError = unsupportedTenantIds[tenantId];
1111
+ if (unsupportedTenantError) {
1112
+ throw new CredentialUnavailableError(unsupportedTenantError);
1113
+ }
1114
+ }
1115
+ const mapVSCodeAuthorityHosts = {
1116
+ AzureCloud: exports.AzureAuthorityHosts.AzurePublicCloud,
1117
+ AzureChina: exports.AzureAuthorityHosts.AzureChina,
1118
+ AzureGermanCloud: exports.AzureAuthorityHosts.AzureGermany,
1119
+ AzureUSGovernment: exports.AzureAuthorityHosts.AzureGovernment
1120
+ };
987
1121
  /**
988
- * MSAL client secret client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
989
- * @internal
1122
+ * Attempts to load a specific property from the VSCode configurations of the current OS.
1123
+ * If it fails at any point, returns undefined.
990
1124
  */
991
- class MsalClientSecret extends MsalNode {
992
- constructor(options) {
993
- super(options);
994
- this.requiresConfidential = true;
995
- this.msalConfig.auth.clientSecret = options.clientSecret;
1125
+ function getPropertyFromVSCode(property) {
1126
+ const settingsPath = ["User", "settings.json"];
1127
+ // Eventually we can add more folders for more versions of VSCode.
1128
+ const vsCodeFolder = "Code";
1129
+ const homedir = os.homedir();
1130
+ function loadProperty(...pathSegments) {
1131
+ const fullPath = path.join(...pathSegments, vsCodeFolder, ...settingsPath);
1132
+ const settings = JSON.parse(fs__default.readFileSync(fullPath, { encoding: "utf8" }));
1133
+ return settings[property];
1134
+ }
1135
+ try {
1136
+ let appData;
1137
+ switch (process.platform) {
1138
+ case "win32":
1139
+ appData = process.env.APPDATA;
1140
+ return appData ? loadProperty(appData) : undefined;
1141
+ case "darwin":
1142
+ return loadProperty(homedir, "Library", "Application Support");
1143
+ case "linux":
1144
+ return loadProperty(homedir, ".config");
1145
+ default:
1146
+ return;
1147
+ }
996
1148
  }
997
- doGetToken(scopes, options = {}) {
998
- return tslib.__awaiter(this, void 0, void 0, function* () {
999
- try {
1000
- const result = yield this.confidentialApp.acquireTokenByClientCredential({
1001
- scopes,
1002
- correlationId: options.correlationId
1003
- });
1004
- // The Client Credential flow does not return an account,
1005
- // so each time getToken gets called, we will have to acquire a new token through the service.
1006
- return this.handleResult(scopes, this.clientId, result || undefined);
1007
- }
1008
- catch (err) {
1009
- throw this.handleError(scopes, err, options);
1010
- }
1011
- });
1149
+ catch (e) {
1150
+ logger$1.info(`Failed to load the Visual Studio Code configuration file. Error: ${e.message}`);
1151
+ return;
1012
1152
  }
1013
1153
  }
1014
-
1015
- // Copyright (c) Microsoft Corporation.
1016
- const logger$2 = credentialLogger("ClientSecretCredential");
1017
1154
  /**
1018
- * Enables authentication to Azure Active Directory using a client secret
1019
- * that was generated for an App Registration. More information on how
1020
- * to configure a client secret can be found here:
1021
- *
1022
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application
1023
- *
1155
+ * Connects to Azure using the credential provided by the VSCode extension 'Azure Account'.
1156
+ * Once the user has logged in via the extension, this credential can share the same refresh token
1157
+ * that is cached by the extension.
1024
1158
  */
1025
- class ClientSecretCredential {
1159
+ class VisualStudioCodeCredential {
1026
1160
  /**
1027
- * Creates an instance of the ClientSecretCredential with the details
1028
- * needed to authenticate against Azure Active Directory with a client
1029
- * secret.
1161
+ * Creates an instance of VisualStudioCodeCredential to use for automatically authenticating via VSCode.
1162
+ *
1163
+ * **Note**: `VisualStudioCodeCredential` is provided by a plugin package:
1164
+ * `@azure/identity-vscode`. If this package is not installed and registered
1165
+ * using the plugin API (`useIdentityPlugin`), then authentication using
1166
+ * `VisualStudioCodeCredential` will not be available.
1030
1167
  *
1031
- * @param tenantId - The Azure Active Directory tenant (directory) ID.
1032
- * @param clientId - The client (application) ID of an App Registration in the tenant.
1033
- * @param clientSecret - A client secret that was generated for the App Registration.
1034
1168
  * @param options - Options for configuring the client which makes the authentication request.
1035
1169
  */
1036
- constructor(tenantId, clientId, clientSecret, options = {}) {
1037
- this.msalFlow = new MsalClientSecret(Object.assign(Object.assign({}, options), { logger: logger$2,
1038
- clientId,
1039
- tenantId,
1040
- clientSecret, tokenCredentialOptions: options }));
1041
- }
1042
- /**
1043
- * Authenticates with Azure Active Directory and returns an access token if
1044
- * successful. If authentication cannot be performed at this time, this method may
1045
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
1046
- * containing failure details will be thrown.
1170
+ constructor(options) {
1171
+ // We want to make sure we use the one assigned by the user on the VSCode settings.
1172
+ // Or just `AzureCloud` by default.
1173
+ this.cloudName = (getPropertyFromVSCode("azure.cloud") || "AzureCloud");
1174
+ // Picking an authority host based on the cloud name.
1175
+ const authorityHost = mapVSCodeAuthorityHosts[this.cloudName];
1176
+ this.identityClient = new IdentityClient(Object.assign({ authorityHost }, options));
1177
+ if (options && options.tenantId) {
1178
+ checkTenantId(logger$1, options.tenantId);
1179
+ this.tenantId = options.tenantId;
1180
+ }
1181
+ else {
1182
+ this.tenantId = CommonTenantId;
1183
+ }
1184
+ checkUnsupportedTenant(this.tenantId);
1185
+ }
1186
+ /**
1187
+ * Runs preparations for any further getToken request.
1188
+ */
1189
+ async prepare() {
1190
+ // Attempts to load the tenant from the VSCode configuration file.
1191
+ const settingsTenant = getPropertyFromVSCode("azure.tenant");
1192
+ if (settingsTenant) {
1193
+ this.tenantId = settingsTenant;
1194
+ }
1195
+ checkUnsupportedTenant(this.tenantId);
1196
+ }
1197
+ /**
1198
+ * Runs preparations for any further getToken, but only once.
1199
+ */
1200
+ prepareOnce() {
1201
+ if (!this.preparePromise) {
1202
+ this.preparePromise = this.prepare();
1203
+ }
1204
+ return this.preparePromise;
1205
+ }
1206
+ /**
1207
+ * Returns the token found by searching VSCode's authentication cache or
1208
+ * returns null if no token could be found.
1047
1209
  *
1048
1210
  * @param scopes - The list of scopes for which the token will have access.
1049
1211
  * @param options - The options used to configure any requests this
1050
- * TokenCredential implementation might make.
1212
+ * `TokenCredential` implementation might make.
1051
1213
  */
1052
- getToken(scopes, options = {}) {
1053
- return tslib.__awaiter(this, void 0, void 0, function* () {
1054
- return trace(`${this.constructor.name}.getToken`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
1055
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1056
- return this.msalFlow.getToken(arrayScopes, newOptions);
1057
- }));
1058
- });
1059
- }
1060
- }
1061
-
1062
- // Copyright (c) Microsoft Corporation.
1063
- /**
1064
- * MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
1065
- * @internal
1066
- */
1067
- class MsalClientCertificate extends MsalNode {
1068
- constructor(options) {
1069
- super(options);
1070
- this.requiresConfidential = true;
1071
- this.sendCertificateChain = options.sendCertificateChain;
1072
- const parts = this.parseCertificate(options.certificatePath);
1073
- this.msalConfig.auth.clientCertificate = {
1074
- thumbprint: parts.thumbprint,
1075
- privateKey: parts.certificateContents,
1076
- x5c: parts.x5c
1077
- };
1078
- }
1079
- parseCertificate(certificatePath) {
1080
- const certificateParts = {};
1081
- certificateParts.certificateContents = fs.readFileSync(certificatePath, "utf8");
1082
- if (this.sendCertificateChain) {
1083
- certificateParts.x5c = certificateParts.certificateContents;
1084
- }
1085
- const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
1086
- const publicKeys = [];
1087
- // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
1088
- let match;
1089
- do {
1090
- match = certificatePattern.exec(certificateParts.certificateContents);
1091
- if (match) {
1092
- publicKeys.push(match[3]);
1093
- }
1094
- } while (match);
1095
- if (publicKeys.length === 0) {
1096
- const error = new Error("The file at the specified path does not contain a PEM-encoded certificate.");
1097
- this.logger.info(formatError("", error));
1214
+ async getToken(scopes, options) {
1215
+ var _a, _b;
1216
+ await this.prepareOnce();
1217
+ const tenantId = processMultiTenantRequest(this.tenantId, options) || this.tenantId;
1218
+ if (findCredentials === undefined) {
1219
+ throw new CredentialUnavailableError([
1220
+ "No implementation of `VisualStudioCodeCredential` is available.",
1221
+ "You must install the identity-vscode plugin package (`npm install --save-dev @azure/identity-vscode`)",
1222
+ "and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling",
1223
+ "`useIdentityPlugin(vsCodePlugin)` before creating a `VisualStudioCodeCredential`."
1224
+ ].join(" "));
1225
+ }
1226
+ let scopeString = typeof scopes === "string" ? scopes : scopes.join(" ");
1227
+ // Check to make sure the scope we get back is a valid scope
1228
+ if (!scopeString.match(/^[0-9a-zA-Z-.:/]+$/)) {
1229
+ const error = new Error("Invalid scope was specified by the user or calling client");
1230
+ logger$1.getToken.info(formatError(scopes, error));
1098
1231
  throw error;
1099
1232
  }
1100
- certificateParts.thumbprint = crypto.createHash("sha1")
1101
- .update(Buffer.from(publicKeys[0], "base64"))
1102
- .digest("hex")
1103
- .toUpperCase();
1104
- return certificateParts;
1105
- }
1106
- doGetToken(scopes, options = {}) {
1107
- return tslib.__awaiter(this, void 0, void 0, function* () {
1108
- try {
1109
- const result = yield this.confidentialApp.acquireTokenByClientCredential({
1110
- scopes,
1111
- correlationId: options.correlationId
1112
- });
1113
- // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential,
1114
- // The Client Credential flow does not return the account information from the authentication service,
1115
- // so each time getToken gets called, we will have to acquire a new token through the service.
1116
- return this.handleResult(scopes, this.clientId, result || undefined);
1233
+ if (scopeString.indexOf("offline_access") < 0) {
1234
+ scopeString += " offline_access";
1235
+ }
1236
+ // findCredentials returns an array similar to:
1237
+ // [
1238
+ // {
1239
+ // account: "",
1240
+ // password: "",
1241
+ // },
1242
+ // /* ... */
1243
+ // ]
1244
+ const credentials = await findCredentials();
1245
+ // If we can't find the credential based on the name, we'll pick the first one available.
1246
+ const { password: refreshToken } = (_b = (_a = credentials.find(({ account }) => account === this.cloudName)) !== null && _a !== void 0 ? _a : credentials[0]) !== null && _b !== void 0 ? _b : {};
1247
+ if (refreshToken) {
1248
+ const tokenResponse = await this.identityClient.refreshAccessToken(tenantId, AzureAccountClientId, scopeString, refreshToken, undefined);
1249
+ if (tokenResponse) {
1250
+ logger$1.getToken.info(formatSuccess(scopes));
1251
+ return tokenResponse.accessToken;
1117
1252
  }
1118
- catch (err) {
1119
- throw this.handleError(scopes, err, options);
1253
+ else {
1254
+ const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Have you connected using the 'Azure Account' extension recently? To troubleshoot, visit https://aka.ms/azsdk/js/identity/visualstudiocodecredential/troubleshoot.");
1255
+ logger$1.getToken.info(formatError(scopes, error));
1256
+ throw error;
1120
1257
  }
1121
- });
1258
+ }
1259
+ else {
1260
+ const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Did you connect using the 'Azure Account' extension? To troubleshoot, visit https://aka.ms/azsdk/js/identity/visualstudiocodecredential/troubleshoot.");
1261
+ logger$1.getToken.info(formatError(scopes, error));
1262
+ throw error;
1263
+ }
1122
1264
  }
1123
1265
  }
1124
1266
 
1125
1267
  // Copyright (c) Microsoft Corporation.
1126
- const logger$3 = credentialLogger("ClientCertificateCredential");
1127
1268
  /**
1128
- * Enables authentication to Azure Active Directory using a PEM-encoded
1129
- * certificate that is assigned to an App Registration. More information
1130
- * on how to configure certificate authentication can be found here:
1269
+ * The context passed to an Identity plugin. This contains objects that
1270
+ * plugins can use to set backend implementations.
1271
+ * @internal
1272
+ */
1273
+ const pluginContext = {
1274
+ cachePluginControl: msalNodeFlowCacheControl,
1275
+ vsCodeCredentialControl: vsCodeCredentialControl
1276
+ };
1277
+ /**
1278
+ * Extend Azure Identity with additional functionality. Pass a plugin from
1279
+ * a plugin package, such as:
1131
1280
  *
1132
- * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad
1281
+ * - `@azure/identity-cache-persistence`: provides persistent token caching
1282
+ * - `@azure/identity-vscode`: provides the dependencies of
1283
+ * `VisualStudioCodeCredential` and enables it
1284
+ *
1285
+ * Example:
1286
+ *
1287
+ * ```javascript
1288
+ * import { cachePersistencePlugin } from "@azure/identity-cache-persistence";
1133
1289
  *
1290
+ * import { useIdentityPlugin, DefaultAzureCredential } from "@azure/identity";
1291
+ * useIdentityPlugin(cachePersistencePlugin);
1292
+ *
1293
+ * // The plugin has the capability to extend `DefaultAzureCredential` and to
1294
+ * // add middleware to the underlying credentials, such as persistence.
1295
+ * const credential = new DefaultAzureCredential({
1296
+ * tokenCachePersistenceOptions: {
1297
+ * enabled: true
1298
+ * }
1299
+ * });
1300
+ * ```
1301
+ *
1302
+ * @param plugin - the plugin to register
1134
1303
  */
1135
- class ClientCertificateCredential {
1304
+ function useIdentityPlugin(plugin) {
1305
+ plugin(pluginContext);
1306
+ }
1307
+
1308
+ // Copyright (c) Microsoft Corporation.
1309
+ /**
1310
+ * @internal
1311
+ */
1312
+ const logger$2 = credentialLogger("ChainedTokenCredential");
1313
+ /**
1314
+ * Enables multiple `TokenCredential` implementations to be tried in order
1315
+ * until one of the getToken methods returns an access token.
1316
+ */
1317
+ class ChainedTokenCredential {
1136
1318
  /**
1137
- * Creates an instance of the ClientCertificateCredential with the details
1138
- * needed to authenticate against Azure Active Directory with a certificate.
1319
+ * Creates an instance of ChainedTokenCredential using the given credentials.
1139
1320
  *
1140
- * @param tenantId - The Azure Active Directory tenant (directory) ID.
1141
- * @param clientId - The client (application) ID of an App Registration in the tenant.
1142
- * @param certificatePath - The path to a PEM-encoded public/private key certificate on the filesystem.
1143
- * @param options - Options for configuring the client which makes the authentication request.
1321
+ * @param sources - `TokenCredential` implementations to be tried in order.
1322
+ *
1323
+ * Example usage:
1324
+ * ```javascript
1325
+ * const firstCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
1326
+ * const secondCredential = new ClientSecretCredential(tenantId, anotherClientId, anotherSecret);
1327
+ * const credentialChain = new ChainedTokenCredential(firstCredential, secondCredential);
1328
+ * ```
1144
1329
  */
1145
- constructor(tenantId, clientId, certificatePath, options = {}) {
1146
- this.msalFlow = new MsalClientCertificate(Object.assign(Object.assign({}, options), { certificatePath,
1147
- logger: logger$3,
1148
- clientId,
1149
- tenantId, sendCertificateChain: options.sendCertificateChain, tokenCredentialOptions: options }));
1330
+ constructor(...sources) {
1331
+ /**
1332
+ * The message to use when the chained token fails to get a token
1333
+ */
1334
+ this.UnavailableMessage = "ChainedTokenCredential => failed to retrieve a token from the included credentials";
1335
+ this._sources = [];
1336
+ this._sources = sources;
1150
1337
  }
1151
1338
  /**
1152
- * Authenticates with Azure Active Directory and returns an access token if
1153
- * successful. If authentication cannot be performed at this time, this method may
1154
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
1155
- * containing failure details will be thrown.
1339
+ * Returns the first access token returned by one of the chained
1340
+ * `TokenCredential` implementations. Throws an {@link AggregateAuthenticationError}
1341
+ * when one or more credentials throws an {@link AuthenticationError} and
1342
+ * no credentials have returned an access token.
1343
+ *
1344
+ * This method is called automatically by Azure SDK client libraries. You may call this method
1345
+ * directly, but you must also handle token caching and token refreshing.
1156
1346
  *
1157
1347
  * @param scopes - The list of scopes for which the token will have access.
1158
1348
  * @param options - The options used to configure any requests this
1159
- * TokenCredential implementation might make.
1349
+ * `TokenCredential` implementation might make.
1160
1350
  */
1161
- getToken(scopes, options = {}) {
1162
- return tslib.__awaiter(this, void 0, void 0, function* () {
1163
- return trace(`${this.constructor.name}.getToken`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
1164
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1165
- return this.msalFlow.getToken(arrayScopes, newOptions);
1166
- }));
1167
- });
1351
+ async getToken(scopes, options) {
1352
+ let token = null;
1353
+ let successfulCredentialName = "";
1354
+ const errors = [];
1355
+ const { span, updatedOptions } = createSpan("ChainedTokenCredential.getToken", options);
1356
+ for (let i = 0; i < this._sources.length && token === null; i++) {
1357
+ try {
1358
+ token = await this._sources[i].getToken(scopes, updatedOptions);
1359
+ successfulCredentialName = this._sources[i].constructor.name;
1360
+ }
1361
+ catch (err) {
1362
+ if (err.name === "CredentialUnavailableError" ||
1363
+ err.name === "AuthenticationRequiredError") {
1364
+ errors.push(err);
1365
+ }
1366
+ else {
1367
+ logger$2.getToken.info(formatError(scopes, err));
1368
+ throw err;
1369
+ }
1370
+ }
1371
+ }
1372
+ if (!token && errors.length > 0) {
1373
+ const err = new AggregateAuthenticationError(errors, "ChainedTokenCredential authentication failed.");
1374
+ span.setStatus({
1375
+ code: coreTracing.SpanStatusCode.ERROR,
1376
+ message: err.message
1377
+ });
1378
+ logger$2.getToken.info(formatError(scopes, err));
1379
+ throw err;
1380
+ }
1381
+ span.end();
1382
+ logger$2.getToken.info(`Result for ${successfulCredentialName}: ${formatSuccess(scopes)}`);
1383
+ if (token === null) {
1384
+ throw new CredentialUnavailableError("Failed to retrieve a valid token");
1385
+ }
1386
+ return token;
1168
1387
  }
1169
1388
  }
1170
1389
 
1171
1390
  // Copyright (c) Microsoft Corporation.
1172
1391
  /**
1173
- * MSAL username and password client. Calls to the MSAL's public application's `acquireTokenByUsernamePassword` during `doGetToken`.
1392
+ * Throws if the received scope is not valid.
1174
1393
  * @internal
1175
1394
  */
1176
- class MsalUsernamePassword extends MsalNode {
1177
- constructor(options) {
1178
- super(options);
1179
- this.username = options.username;
1180
- this.password = options.password;
1395
+ function ensureValidScope(scope, logger) {
1396
+ if (!scope.match(/^[0-9a-zA-Z-.:/]+$/)) {
1397
+ const error = new Error("Invalid scope was specified by the user or calling client");
1398
+ logger.getToken.info(formatError(scope, error));
1399
+ throw error;
1181
1400
  }
1182
- doGetToken(scopes, options) {
1183
- return tslib.__awaiter(this, void 0, void 0, function* () {
1401
+ }
1402
+ /**
1403
+ * Returns the resource out of a scope.
1404
+ * @internal
1405
+ */
1406
+ function getScopeResource(scope) {
1407
+ return scope.replace(/\/.default$/, "");
1408
+ }
1409
+
1410
+ // Copyright (c) Microsoft Corporation.
1411
+ /**
1412
+ * Mockable reference to the CLI credential cliCredentialFunctions
1413
+ * @internal
1414
+ */
1415
+ const cliCredentialInternals = {
1416
+ /**
1417
+ * @internal
1418
+ */
1419
+ getSafeWorkingDir() {
1420
+ if (process.platform === "win32") {
1421
+ if (!process.env.SystemRoot) {
1422
+ throw new Error("Azure CLI credential expects a 'SystemRoot' environment variable");
1423
+ }
1424
+ return process.env.SystemRoot;
1425
+ }
1426
+ else {
1427
+ return "/bin";
1428
+ }
1429
+ },
1430
+ /**
1431
+ * Gets the access token from Azure CLI
1432
+ * @param resource - The resource to use when getting the token
1433
+ * @internal
1434
+ */
1435
+ async getAzureCliAccessToken(resource, tenantId) {
1436
+ let tenantSection = [];
1437
+ if (tenantId) {
1438
+ tenantSection = ["--tenant", tenantId];
1439
+ }
1440
+ return new Promise((resolve, reject) => {
1184
1441
  try {
1185
- const requestOptions = {
1186
- scopes,
1187
- username: this.username,
1188
- password: this.password,
1189
- correlationId: options === null || options === void 0 ? void 0 : options.correlationId
1190
- };
1191
- const result = yield this.publicApp.acquireTokenByUsernamePassword(requestOptions);
1192
- return this.handleResult(scopes, this.clientId, result || undefined);
1442
+ child_process__default.execFile("az", [
1443
+ "account",
1444
+ "get-access-token",
1445
+ "--output",
1446
+ "json",
1447
+ "--resource",
1448
+ resource,
1449
+ ...tenantSection
1450
+ ], { cwd: cliCredentialInternals.getSafeWorkingDir() }, (error, stdout, stderr) => {
1451
+ resolve({ stdout: stdout, stderr: stderr, error });
1452
+ });
1193
1453
  }
1194
- catch (error) {
1195
- throw this.handleError(scopes, error, options);
1454
+ catch (err) {
1455
+ reject(err);
1196
1456
  }
1197
1457
  });
1198
1458
  }
1199
- }
1200
-
1201
- // Copyright (c) Microsoft Corporation.
1202
- const logger$4 = credentialLogger("UsernamePasswordCredential");
1459
+ };
1460
+ const logger$3 = credentialLogger("AzureCliCredential");
1203
1461
  /**
1204
- * Enables authentication to Azure Active Directory with a user's
1205
- * username and password. This credential requires a high degree of
1206
- * trust so you should only use it when other, more secure credential
1207
- * types can't be used.
1462
+ * This credential will use the currently logged-in user login information
1463
+ * via the Azure CLI ('az') commandline tool.
1464
+ * To do so, it will read the user access token and expire time
1465
+ * with Azure CLI command "az account get-access-token".
1208
1466
  */
1209
- // We'll be using InteractiveCredential as the base of this class, which requires us to support authenticate(),
1210
- // to reduce the number of times we send the password over the network.
1211
- class UsernamePasswordCredential {
1467
+ class AzureCliCredential {
1212
1468
  /**
1213
- * Creates an instance of the UsernamePasswordCredential with the details
1214
- * needed to authenticate against Azure Active Directory with a username
1215
- * and password.
1469
+ * Creates an instance of the {@link AzureCliCredential}.
1216
1470
  *
1217
- * @param tenantId - The Azure Active Directory tenant (directory).
1218
- * @param clientId - The client (application) ID of an App Registration in the tenant.
1219
- * @param username - The user account's e-mail address (user name).
1220
- * @param password - The user account's account password
1221
- * @param options - Options for configuring the client which makes the authentication request.
1471
+ * To use this credential, ensure that you have already logged
1472
+ * in via the 'az' tool using the command "az login" from the commandline.
1473
+ *
1474
+ * @param options - Options, to optionally allow multi-tenant requests.
1222
1475
  */
1223
- constructor(tenantId, clientId, username, password, options = {}) {
1224
- this.msalFlow = new MsalUsernamePassword(Object.assign(Object.assign({}, options), { logger: logger$4,
1225
- clientId,
1226
- tenantId,
1227
- username,
1228
- password, tokenCredentialOptions: options || {} }));
1476
+ constructor(options) {
1477
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
1229
1478
  }
1230
1479
  /**
1231
- * Authenticates with Azure Active Directory and returns an access token if
1232
- * successful. If authentication cannot be performed at this time, this method may
1233
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
1234
- * containing failure details will be thrown.
1235
- *
1236
- * If the user provided the option `disableAutomaticAuthentication`,
1237
- * once the token can't be retrieved silently,
1238
- * this method won't attempt to request user interaction to retrieve the token.
1480
+ * Authenticates with Azure Active Directory and returns an access token if successful.
1481
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
1239
1482
  *
1240
1483
  * @param scopes - The list of scopes for which the token will have access.
1241
1484
  * @param options - The options used to configure any requests this
1242
1485
  * TokenCredential implementation might make.
1243
1486
  */
1244
- getToken(scopes, options = {}) {
1245
- return tslib.__awaiter(this, void 0, void 0, function* () {
1246
- return trace(`${this.constructor.name}.getToken`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
1247
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1248
- return this.msalFlow.getToken(arrayScopes, newOptions);
1249
- }));
1250
- });
1487
+ async getToken(scopes, options) {
1488
+ const tenantId = processMultiTenantRequest(this.tenantId, options);
1489
+ if (tenantId) {
1490
+ checkTenantId(logger$3, tenantId);
1491
+ }
1492
+ const scope = typeof scopes === "string" ? scopes : scopes[0];
1493
+ logger$3.getToken.info(`Using the scope ${scope}`);
1494
+ ensureValidScope(scope, logger$3);
1495
+ const resource = getScopeResource(scope);
1496
+ let responseData = "";
1497
+ const { span } = createSpan("AzureCliCredential.getToken", options);
1498
+ try {
1499
+ const obj = await cliCredentialInternals.getAzureCliAccessToken(resource, tenantId);
1500
+ if (obj.stderr) {
1501
+ const isLoginError = obj.stderr.match("(.*)az login(.*)");
1502
+ const isNotInstallError = obj.stderr.match("az:(.*)not found") || obj.stderr.startsWith("'az' is not recognized");
1503
+ if (isNotInstallError) {
1504
+ 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'.");
1505
+ logger$3.getToken.info(formatError(scopes, error));
1506
+ throw error;
1507
+ }
1508
+ else if (isLoginError) {
1509
+ const error = new CredentialUnavailableError("Please run 'az login' from a command prompt to authenticate before using this credential.");
1510
+ logger$3.getToken.info(formatError(scopes, error));
1511
+ throw error;
1512
+ }
1513
+ const error = new CredentialUnavailableError(obj.stderr);
1514
+ logger$3.getToken.info(formatError(scopes, error));
1515
+ throw error;
1516
+ }
1517
+ else {
1518
+ responseData = obj.stdout;
1519
+ const response = JSON.parse(responseData);
1520
+ logger$3.getToken.info(formatSuccess(scopes));
1521
+ const returnValue = {
1522
+ token: response.accessToken,
1523
+ expiresOnTimestamp: new Date(response.expiresOn).getTime()
1524
+ };
1525
+ return returnValue;
1526
+ }
1527
+ }
1528
+ catch (err) {
1529
+ const error = new Error(err.message || "Unknown error while trying to retrieve the access token");
1530
+ span.setStatus({
1531
+ code: coreTracing.SpanStatusCode.ERROR,
1532
+ message: error.message
1533
+ });
1534
+ logger$3.getToken.info(formatError(scopes, error));
1535
+ throw error;
1536
+ }
1251
1537
  }
1252
1538
  }
1253
1539
 
1254
1540
  // Copyright (c) Microsoft Corporation.
1255
1541
  /**
1256
- * Contains the list of all supported environment variable names so that an
1257
- * appropriate error message can be generated when no credentials can be
1258
- * configured.
1259
- *
1542
+ * Easy to mock childProcess utils.
1260
1543
  * @internal
1261
1544
  */
1262
- const AllSupportedEnvironmentVariables = [
1263
- "AZURE_TENANT_ID",
1264
- "AZURE_CLIENT_ID",
1265
- "AZURE_CLIENT_SECRET",
1266
- "AZURE_CLIENT_CERTIFICATE_PATH",
1267
- "AZURE_USERNAME",
1268
- "AZURE_PASSWORD"
1269
- ];
1270
- const logger$5 = credentialLogger("EnvironmentCredential");
1271
- /**
1272
- * Enables authentication to Azure Active Directory using client secret
1273
- * details configured in the following environment variables:
1274
- *
1275
- * - AZURE_TENANT_ID: The Azure Active Directory tenant (directory) ID.
1276
- * - AZURE_CLIENT_ID: The client (application) ID of an App Registration in the tenant.
1277
- * - AZURE_CLIENT_SECRET: A client secret that was generated for the App Registration.
1278
- *
1279
- * This credential ultimately uses a {@link ClientSecretCredential} to
1280
- * perform the authentication using these details. Please consult the
1281
- * documentation of that class for more details.
1282
- */
1283
- class EnvironmentCredential {
1545
+ const processUtils = {
1284
1546
  /**
1285
- * Creates an instance of the EnvironmentCredential class and reads
1286
- * client secret details from environment variables. If the expected
1287
- * environment variables are not found at this time, the getToken method
1288
- * will return null when invoked.
1289
- *
1290
- * @param options - Options for configuring the client which makes the authentication request.
1547
+ * Promisifying childProcess.execFile
1548
+ * @internal
1291
1549
  */
1292
- constructor(options) {
1293
- // Keep track of any missing environment variables for error details
1294
- this._credential = undefined;
1295
- const assigned = processEnvVars(AllSupportedEnvironmentVariables).assigned.join(", ");
1296
- logger$5.info(`Found the following environment variables: ${assigned}`);
1297
- const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
1298
- if (tenantId) {
1299
- checkTenantId(logger$5, tenantId);
1300
- }
1301
- if (tenantId && clientId && clientSecret) {
1302
- logger$5.info(`Invoking ClientSecretCredential with tenant ID: ${tenantId}, clientId: ${clientId} and clientSecret: [REDACTED]`);
1550
+ execFile(file, params, options) {
1551
+ return new Promise((resolve, reject) => {
1552
+ child_process.execFile(file, params, options, (error, stdout, stderr) => {
1553
+ if (Buffer.isBuffer(stdout)) {
1554
+ stdout = stdout.toString("utf8");
1555
+ }
1556
+ if (Buffer.isBuffer(stderr)) {
1557
+ stderr = stderr.toString("utf8");
1558
+ }
1559
+ if (stderr || error) {
1560
+ reject(stderr ? new Error(stderr) : error);
1561
+ }
1562
+ else {
1563
+ resolve(stdout);
1564
+ }
1565
+ });
1566
+ });
1567
+ }
1568
+ };
1569
+
1570
+ // Copyright (c) Microsoft Corporation.
1571
+ const logger$4 = credentialLogger("AzurePowerShellCredential");
1572
+ const isWindows = process.platform === "win32";
1573
+ /**
1574
+ * Returns a platform-appropriate command name by appending ".exe" on Windows.
1575
+ *
1576
+ * @internal
1577
+ */
1578
+ function formatCommand(commandName) {
1579
+ if (isWindows) {
1580
+ return `${commandName}.exe`;
1581
+ }
1582
+ else {
1583
+ return commandName;
1584
+ }
1585
+ }
1586
+ /**
1587
+ * Receives a list of commands to run, executes them, then returns the outputs.
1588
+ * If anything fails, an error is thrown.
1589
+ * @internal
1590
+ */
1591
+ async function runCommands(commands) {
1592
+ const results = [];
1593
+ for (const command of commands) {
1594
+ const [file, ...parameters] = command;
1595
+ const result = (await processUtils.execFile(file, parameters, { encoding: "utf8" }));
1596
+ results.push(result);
1597
+ }
1598
+ return results;
1599
+ }
1600
+ /**
1601
+ * Known PowerShell errors
1602
+ * @internal
1603
+ */
1604
+ const powerShellErrors = {
1605
+ login: "Run Connect-AzAccount to login",
1606
+ 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"
1607
+ };
1608
+ /**
1609
+ * Messages to use when throwing in this credential.
1610
+ * @internal
1611
+ */
1612
+ const powerShellPublicErrorMessages = {
1613
+ login: "Please run 'Connect-AzAccount' from PowerShell to authenticate before using this credential.",
1614
+ 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".`,
1615
+ troubleshoot: `To troubleshoot, visit https://aka.ms/azsdk/js/identity/powershellcredential/troubleshoot.`
1616
+ };
1617
+ // PowerShell Azure User not logged in error check.
1618
+ const isLoginError = (err) => err.message.match(`(.*)${powerShellErrors.login}(.*)`);
1619
+ // Az Module not Installed in Azure PowerShell check.
1620
+ const isNotInstalledError = (err) => err.message.match(powerShellErrors.installed);
1621
+ /**
1622
+ * The PowerShell commands to be tried, in order.
1623
+ *
1624
+ * @internal
1625
+ */
1626
+ const commandStack = [formatCommand("pwsh")];
1627
+ if (isWindows) {
1628
+ commandStack.push(formatCommand("powershell"));
1629
+ }
1630
+ /**
1631
+ * This credential will use the currently logged-in user information from the
1632
+ * Azure PowerShell module. To do so, it will read the user access token and
1633
+ * expire time with Azure PowerShell command `Get-AzAccessToken -ResourceUrl {ResourceScope}`
1634
+ */
1635
+ class AzurePowerShellCredential {
1636
+ /**
1637
+ * Creates an instance of the {@link AzurePowerShellCredential}.
1638
+ *
1639
+ * To use this credential:
1640
+ * - Install the Azure Az PowerShell module with:
1641
+ * `Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force`.
1642
+ * - You have already logged in to Azure PowerShell using the command
1643
+ * `Connect-AzAccount` from the command line.
1644
+ *
1645
+ * @param options - Options, to optionally allow multi-tenant requests.
1646
+ */
1647
+ constructor(options) {
1648
+ this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId;
1649
+ }
1650
+ /**
1651
+ * Gets the access token from Azure PowerShell
1652
+ * @param resource - The resource to use when getting the token
1653
+ */
1654
+ async getAzurePowerShellAccessToken(resource, tenantId) {
1655
+ // Clone the stack to avoid mutating it while iterating
1656
+ for (const powerShellCommand of [...commandStack]) {
1657
+ try {
1658
+ await runCommands([[powerShellCommand, "/?"]]);
1659
+ }
1660
+ catch (e) {
1661
+ // Remove this credential from the original stack so that we don't try it again.
1662
+ commandStack.shift();
1663
+ continue;
1664
+ }
1665
+ let tenantSection = "";
1666
+ if (tenantId) {
1667
+ tenantSection = `-TenantId "${tenantId}"`;
1668
+ }
1669
+ const results = await runCommands([
1670
+ [
1671
+ powerShellCommand,
1672
+ "-Command",
1673
+ "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru"
1674
+ ],
1675
+ [
1676
+ powerShellCommand,
1677
+ "-Command",
1678
+ `Get-AzAccessToken ${tenantSection} -ResourceUrl "${resource}" | ConvertTo-Json`
1679
+ ]
1680
+ ]);
1681
+ const result = results[1];
1682
+ try {
1683
+ return JSON.parse(result);
1684
+ }
1685
+ catch (e) {
1686
+ throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
1687
+ }
1688
+ }
1689
+ throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`);
1690
+ }
1691
+ /**
1692
+ * Authenticates with Azure Active Directory and returns an access token if successful.
1693
+ * If the authentication cannot be performed through PowerShell, a {@link CredentialUnavailableError} will be thrown.
1694
+ *
1695
+ * @param scopes - The list of scopes for which the token will have access.
1696
+ * @param options - The options used to configure any requests this TokenCredential implementation might make.
1697
+ */
1698
+ async getToken(scopes, options = {}) {
1699
+ return trace(`${this.constructor.name}.getToken`, options, async () => {
1700
+ const tenantId = processMultiTenantRequest(this.tenantId, options);
1701
+ if (tenantId) {
1702
+ checkTenantId(logger$4, tenantId);
1703
+ }
1704
+ const scope = typeof scopes === "string" ? scopes : scopes[0];
1705
+ ensureValidScope(scope, logger$4);
1706
+ logger$4.getToken.info(`Using the scope ${scope}`);
1707
+ const resource = getScopeResource(scope);
1708
+ try {
1709
+ const response = await this.getAzurePowerShellAccessToken(resource, tenantId);
1710
+ logger$4.getToken.info(formatSuccess(scopes));
1711
+ return {
1712
+ token: response.Token,
1713
+ expiresOnTimestamp: new Date(response.ExpiresOn).getTime()
1714
+ };
1715
+ }
1716
+ catch (err) {
1717
+ if (isNotInstalledError(err)) {
1718
+ const error = new CredentialUnavailableError(powerShellPublicErrorMessages.installed);
1719
+ logger$4.getToken.info(formatError(scope, error));
1720
+ throw error;
1721
+ }
1722
+ else if (isLoginError(err)) {
1723
+ const error = new CredentialUnavailableError(powerShellPublicErrorMessages.login);
1724
+ logger$4.getToken.info(formatError(scope, error));
1725
+ throw error;
1726
+ }
1727
+ const error = new CredentialUnavailableError(`${err}. ${powerShellPublicErrorMessages.troubleshoot}`);
1728
+ logger$4.getToken.info(formatError(scope, error));
1729
+ throw error;
1730
+ }
1731
+ });
1732
+ }
1733
+ }
1734
+
1735
+ // Copyright (c) Microsoft Corporation.
1736
+ /**
1737
+ * MSAL client secret client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
1738
+ * @internal
1739
+ */
1740
+ class MsalClientSecret extends MsalNode {
1741
+ constructor(options) {
1742
+ super(options);
1743
+ this.requiresConfidential = true;
1744
+ this.msalConfig.auth.clientSecret = options.clientSecret;
1745
+ }
1746
+ async doGetToken(scopes, options = {}) {
1747
+ try {
1748
+ const result = await this.confidentialApp.acquireTokenByClientCredential({
1749
+ scopes,
1750
+ correlationId: options.correlationId,
1751
+ azureRegion: this.azureRegion,
1752
+ authority: options.authority
1753
+ });
1754
+ // The Client Credential flow does not return an account,
1755
+ // so each time getToken gets called, we will have to acquire a new token through the service.
1756
+ return this.handleResult(scopes, this.clientId, result || undefined);
1757
+ }
1758
+ catch (err) {
1759
+ throw this.handleError(scopes, err, options);
1760
+ }
1761
+ }
1762
+ }
1763
+
1764
+ // Copyright (c) Microsoft Corporation.
1765
+ const logger$5 = credentialLogger("ClientSecretCredential");
1766
+ /**
1767
+ * Enables authentication to Azure Active Directory using a client secret
1768
+ * that was generated for an App Registration. More information on how
1769
+ * to configure a client secret can be found here:
1770
+ *
1771
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application
1772
+ *
1773
+ */
1774
+ class ClientSecretCredential {
1775
+ /**
1776
+ * Creates an instance of the ClientSecretCredential with the details
1777
+ * needed to authenticate against Azure Active Directory with a client
1778
+ * secret.
1779
+ *
1780
+ * @param tenantId - The Azure Active Directory tenant (directory) ID.
1781
+ * @param clientId - The client (application) ID of an App Registration in the tenant.
1782
+ * @param clientSecret - A client secret that was generated for the App Registration.
1783
+ * @param options - Options for configuring the client which makes the authentication request.
1784
+ */
1785
+ constructor(tenantId, clientId, clientSecret, options = {}) {
1786
+ if (!tenantId || !clientId || !clientSecret) {
1787
+ throw new Error("ClientSecretCredential: tenantId, clientId, and clientSecret are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
1788
+ }
1789
+ this.msalFlow = new MsalClientSecret(Object.assign(Object.assign({}, options), { logger: logger$5,
1790
+ clientId,
1791
+ tenantId,
1792
+ clientSecret, tokenCredentialOptions: options }));
1793
+ }
1794
+ /**
1795
+ * Authenticates with Azure Active Directory and returns an access token if successful.
1796
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
1797
+ *
1798
+ * @param scopes - The list of scopes for which the token will have access.
1799
+ * @param options - The options used to configure any requests this
1800
+ * TokenCredential implementation might make.
1801
+ */
1802
+ async getToken(scopes, options = {}) {
1803
+ return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
1804
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1805
+ return this.msalFlow.getToken(arrayScopes, newOptions);
1806
+ });
1807
+ }
1808
+ }
1809
+
1810
+ // Copyright (c) Microsoft Corporation.
1811
+ const readFileAsync = util.promisify(fs.readFile);
1812
+ /**
1813
+ * Tries to asynchronously load a certificate from the given path.
1814
+ *
1815
+ * @param configuration - Either the PEM value or the path to the certificate.
1816
+ * @param sendCertificateChain - Option to include x5c header for SubjectName and Issuer name authorization.
1817
+ * @returns - The certificate parts, or `undefined` if the certificate could not be loaded.
1818
+ * @internal
1819
+ */
1820
+ async function parseCertificate(configuration, sendCertificateChain) {
1821
+ const certificateParts = {};
1822
+ certificateParts.certificateContents =
1823
+ configuration.certificate || (await readFileAsync(configuration.certificatePath, "utf8"));
1824
+ if (sendCertificateChain) {
1825
+ certificateParts.x5c = certificateParts.certificateContents;
1826
+ }
1827
+ const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
1828
+ const publicKeys = [];
1829
+ // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
1830
+ let match;
1831
+ do {
1832
+ match = certificatePattern.exec(certificateParts.certificateContents);
1833
+ if (match) {
1834
+ publicKeys.push(match[3]);
1835
+ }
1836
+ } while (match);
1837
+ if (publicKeys.length === 0) {
1838
+ throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
1839
+ }
1840
+ certificateParts.thumbprint = crypto.createHash("sha1")
1841
+ .update(Buffer.from(publicKeys[0], "base64"))
1842
+ .digest("hex")
1843
+ .toUpperCase();
1844
+ return certificateParts;
1845
+ }
1846
+ /**
1847
+ * MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
1848
+ * @internal
1849
+ */
1850
+ class MsalClientCertificate extends MsalNode {
1851
+ constructor(options) {
1852
+ super(options);
1853
+ this.requiresConfidential = true;
1854
+ this.configuration = options.configuration;
1855
+ this.sendCertificateChain = options.sendCertificateChain;
1856
+ }
1857
+ // Changing the MSAL configuration asynchronously
1858
+ async init(options) {
1859
+ try {
1860
+ const parts = await parseCertificate(this.configuration, this.sendCertificateChain);
1861
+ this.msalConfig.auth.clientCertificate = {
1862
+ thumbprint: parts.thumbprint,
1863
+ privateKey: parts.certificateContents,
1864
+ x5c: parts.x5c
1865
+ };
1866
+ }
1867
+ catch (error) {
1868
+ this.logger.info(formatError("", error));
1869
+ throw error;
1870
+ }
1871
+ return super.init(options);
1872
+ }
1873
+ async doGetToken(scopes, options = {}) {
1874
+ try {
1875
+ const result = await this.confidentialApp.acquireTokenByClientCredential({
1876
+ scopes,
1877
+ correlationId: options.correlationId,
1878
+ azureRegion: this.azureRegion,
1879
+ authority: options.authority
1880
+ });
1881
+ // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential,
1882
+ // The Client Credential flow does not return the account information from the authentication service,
1883
+ // so each time getToken gets called, we will have to acquire a new token through the service.
1884
+ return this.handleResult(scopes, this.clientId, result || undefined);
1885
+ }
1886
+ catch (err) {
1887
+ throw this.handleError(scopes, err, options);
1888
+ }
1889
+ }
1890
+ }
1891
+
1892
+ // Copyright (c) Microsoft Corporation.
1893
+ const credentialName = "ClientCertificateCredential";
1894
+ const logger$6 = credentialLogger(credentialName);
1895
+ /**
1896
+ * Enables authentication to Azure Active Directory using a PEM-encoded
1897
+ * certificate that is assigned to an App Registration. More information
1898
+ * on how to configure certificate authentication can be found here:
1899
+ *
1900
+ * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad
1901
+ *
1902
+ */
1903
+ class ClientCertificateCredential {
1904
+ constructor(tenantId, clientId, certificatePathOrConfiguration, options = {}) {
1905
+ if (!tenantId || !clientId) {
1906
+ throw new Error(`${credentialName}: tenantId and clientId are required parameters.`);
1907
+ }
1908
+ const configuration = Object.assign({}, (typeof certificatePathOrConfiguration === "string"
1909
+ ? {
1910
+ certificatePath: certificatePathOrConfiguration
1911
+ }
1912
+ : certificatePathOrConfiguration));
1913
+ if (!configuration || !(configuration.certificate || configuration.certificatePath)) {
1914
+ throw new Error(`${credentialName}: 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.`);
1915
+ }
1916
+ if (configuration.certificate && configuration.certificatePath) {
1917
+ throw new Error(`${credentialName}: 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.`);
1918
+ }
1919
+ this.msalFlow = new MsalClientCertificate(Object.assign(Object.assign({}, options), { configuration,
1920
+ logger: logger$6,
1921
+ clientId,
1922
+ tenantId, sendCertificateChain: options.sendCertificateChain, tokenCredentialOptions: options }));
1923
+ }
1924
+ /**
1925
+ * Authenticates with Azure Active Directory and returns an access token if successful.
1926
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
1927
+ *
1928
+ * @param scopes - The list of scopes for which the token will have access.
1929
+ * @param options - The options used to configure any requests this
1930
+ * TokenCredential implementation might make.
1931
+ */
1932
+ async getToken(scopes, options = {}) {
1933
+ return trace(`${credentialName}.getToken`, options, async (newOptions) => {
1934
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
1935
+ return this.msalFlow.getToken(arrayScopes, newOptions);
1936
+ });
1937
+ }
1938
+ }
1939
+
1940
+ // Copyright (c) Microsoft Corporation.
1941
+ /**
1942
+ * MSAL username and password client. Calls to the MSAL's public application's `acquireTokenByUsernamePassword` during `doGetToken`.
1943
+ * @internal
1944
+ */
1945
+ class MsalUsernamePassword extends MsalNode {
1946
+ constructor(options) {
1947
+ super(options);
1948
+ this.username = options.username;
1949
+ this.password = options.password;
1950
+ }
1951
+ async doGetToken(scopes, options) {
1952
+ try {
1953
+ const requestOptions = {
1954
+ scopes,
1955
+ username: this.username,
1956
+ password: this.password,
1957
+ correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
1958
+ authority: options === null || options === void 0 ? void 0 : options.authority
1959
+ };
1960
+ const result = await this.publicApp.acquireTokenByUsernamePassword(requestOptions);
1961
+ return this.handleResult(scopes, this.clientId, result || undefined);
1962
+ }
1963
+ catch (error) {
1964
+ throw this.handleError(scopes, error, options);
1965
+ }
1966
+ }
1967
+ }
1968
+
1969
+ // Copyright (c) Microsoft Corporation.
1970
+ const logger$7 = credentialLogger("UsernamePasswordCredential");
1971
+ /**
1972
+ * Enables authentication to Azure Active Directory with a user's
1973
+ * username and password. This credential requires a high degree of
1974
+ * trust so you should only use it when other, more secure credential
1975
+ * types can't be used.
1976
+ */
1977
+ class UsernamePasswordCredential {
1978
+ /**
1979
+ * Creates an instance of the UsernamePasswordCredential with the details
1980
+ * needed to authenticate against Azure Active Directory with a username
1981
+ * and password.
1982
+ *
1983
+ * @param tenantId - The Azure Active Directory tenant (directory).
1984
+ * @param clientId - The client (application) ID of an App Registration in the tenant.
1985
+ * @param username - The user account's e-mail address (user name).
1986
+ * @param password - The user account's account password
1987
+ * @param options - Options for configuring the client which makes the authentication request.
1988
+ */
1989
+ constructor(tenantId, clientId, username, password, options = {}) {
1990
+ if (!tenantId || !clientId || !username || !password) {
1991
+ throw new Error("UsernamePasswordCredential: tenantId, clientId, username and password are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
1992
+ }
1993
+ this.msalFlow = new MsalUsernamePassword(Object.assign(Object.assign({}, options), { logger: logger$7,
1994
+ clientId,
1995
+ tenantId,
1996
+ username,
1997
+ password, tokenCredentialOptions: options || {} }));
1998
+ }
1999
+ /**
2000
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2001
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2002
+ *
2003
+ * If the user provided the option `disableAutomaticAuthentication`,
2004
+ * once the token can't be retrieved silently,
2005
+ * this method won't attempt to request user interaction to retrieve the token.
2006
+ *
2007
+ * @param scopes - The list of scopes for which the token will have access.
2008
+ * @param options - The options used to configure any requests this
2009
+ * TokenCredential implementation might make.
2010
+ */
2011
+ async getToken(scopes, options = {}) {
2012
+ return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
2013
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2014
+ return this.msalFlow.getToken(arrayScopes, newOptions);
2015
+ });
2016
+ }
2017
+ }
2018
+
2019
+ // Copyright (c) Microsoft Corporation.
2020
+ /**
2021
+ * Contains the list of all supported environment variable names so that an
2022
+ * appropriate error message can be generated when no credentials can be
2023
+ * configured.
2024
+ *
2025
+ * @internal
2026
+ */
2027
+ const AllSupportedEnvironmentVariables = [
2028
+ "AZURE_TENANT_ID",
2029
+ "AZURE_CLIENT_ID",
2030
+ "AZURE_CLIENT_SECRET",
2031
+ "AZURE_CLIENT_CERTIFICATE_PATH",
2032
+ "AZURE_USERNAME",
2033
+ "AZURE_PASSWORD"
2034
+ ];
2035
+ const logger$8 = credentialLogger("EnvironmentCredential");
2036
+ /**
2037
+ * Enables authentication to Azure Active Directory using client secret
2038
+ * details configured in environment variables
2039
+ */
2040
+ class EnvironmentCredential {
2041
+ /**
2042
+ * Creates an instance of the EnvironmentCredential class and decides what credential to use depending on the available environment variables.
2043
+ *
2044
+ * Required environment variables:
2045
+ * - `AZURE_TENANT_ID`: The Azure Active Directory tenant (directory) ID.
2046
+ * - `AZURE_CLIENT_ID`: The client (application) ID of an App Registration in the tenant.
2047
+ *
2048
+ * Environment variables used for client credential authentication:
2049
+ * - `AZURE_CLIENT_SECRET`: A client secret that was generated for the App Registration.
2050
+ * - `AZURE_CLIENT_CERTIFICATE_PATH`: The path to a PEM certificate to use during the authentication, instead of the client secret.
2051
+ *
2052
+ * Alternatively, users can provide environment variables for username and password authentication:
2053
+ * - `AZURE_USERNAME`: Username to authenticate with.
2054
+ * - `AZURE_PASSWORD`: Password to authenticate with.
2055
+ *
2056
+ * If the environment variables required to perform the authentication are missing, a {@link CredentialUnavailableError} will be thrown.
2057
+ * If the authentication fails, or if there's an unknown error, an {@link AuthenticationError} will be thrown.
2058
+ *
2059
+ * @param options - Options for configuring the client which makes the authentication request.
2060
+ */
2061
+ constructor(options) {
2062
+ // Keep track of any missing environment variables for error details
2063
+ this._credential = undefined;
2064
+ const assigned = processEnvVars(AllSupportedEnvironmentVariables).assigned.join(", ");
2065
+ logger$8.info(`Found the following environment variables: ${assigned}`);
2066
+ const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
2067
+ if (tenantId) {
2068
+ checkTenantId(logger$8, tenantId);
2069
+ }
2070
+ if (tenantId && clientId && clientSecret) {
2071
+ logger$8.info(`Invoking ClientSecretCredential with tenant ID: ${tenantId}, clientId: ${clientId} and clientSecret: [REDACTED]`);
1303
2072
  this._credential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
1304
2073
  return;
1305
2074
  }
1306
2075
  const certificatePath = process.env.AZURE_CLIENT_CERTIFICATE_PATH;
1307
2076
  if (tenantId && clientId && certificatePath) {
1308
- logger$5.info(`Invoking ClientCertificateCredential with tenant ID: ${tenantId}, clientId: ${clientId} and certificatePath: ${certificatePath}`);
1309
- this._credential = new ClientCertificateCredential(tenantId, clientId, certificatePath, options);
2077
+ logger$8.info(`Invoking ClientCertificateCredential with tenant ID: ${tenantId}, clientId: ${clientId} and certificatePath: ${certificatePath}`);
2078
+ this._credential = new ClientCertificateCredential(tenantId, clientId, { certificatePath }, options);
1310
2079
  return;
1311
2080
  }
1312
2081
  const username = process.env.AZURE_USERNAME;
1313
2082
  const password = process.env.AZURE_PASSWORD;
1314
2083
  if (tenantId && clientId && username && password) {
1315
- logger$5.info(`Invoking UsernamePasswordCredential with tenant ID: ${tenantId}, clientId: ${clientId} and username: ${username}`);
2084
+ logger$8.info(`Invoking UsernamePasswordCredential with tenant ID: ${tenantId}, clientId: ${clientId} and username: ${username}`);
1316
2085
  this._credential = new UsernamePasswordCredential(tenantId, clientId, username, password, options);
1317
2086
  }
1318
2087
  }
@@ -1322,29 +2091,27 @@ class EnvironmentCredential {
1322
2091
  * @param scopes - The list of scopes for which the token will have access.
1323
2092
  * @param options - Optional parameters. See {@link GetTokenOptions}.
1324
2093
  */
1325
- getToken(scopes, options = {}) {
1326
- return tslib.__awaiter(this, void 0, void 0, function* () {
1327
- return trace("EnvironmentCredential.getToken", options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
1328
- if (this._credential) {
1329
- try {
1330
- const result = yield this._credential.getToken(scopes, newOptions);
1331
- logger$5.getToken.info(formatSuccess(scopes));
1332
- return result;
1333
- }
1334
- catch (err) {
1335
- const authenticationError = new AuthenticationError(400, {
1336
- error: "EnvironmentCredential authentication failed.",
1337
- error_description: err.message
1338
- .toString()
1339
- .split("More details:")
1340
- .join("")
1341
- });
1342
- logger$5.getToken.info(formatError(scopes, authenticationError));
1343
- throw authenticationError;
1344
- }
2094
+ async getToken(scopes, options = {}) {
2095
+ return trace("EnvironmentCredential.getToken", options, async (newOptions) => {
2096
+ if (this._credential) {
2097
+ try {
2098
+ const result = await this._credential.getToken(scopes, newOptions);
2099
+ logger$8.getToken.info(formatSuccess(scopes));
2100
+ return result;
1345
2101
  }
1346
- throw new CredentialUnavailableError("EnvironmentCredential is unavailable. No underlying credential could be used.");
1347
- }));
2102
+ catch (err) {
2103
+ const authenticationError = new AuthenticationError(400, {
2104
+ error: "EnvironmentCredential authentication failed. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.",
2105
+ error_description: err.message
2106
+ .toString()
2107
+ .split("More details:")
2108
+ .join("")
2109
+ });
2110
+ logger$8.getToken.info(formatError(scopes, authenticationError));
2111
+ throw authenticationError;
2112
+ }
2113
+ }
2114
+ throw new CredentialUnavailableError("EnvironmentCredential is unavailable. No underlying credential could be used. To troubleshoot, visit https://aka.ms/azsdk/js/identity/environmentcredential/troubleshoot.");
1348
2115
  });
1349
2116
  }
1350
2117
  }
@@ -1352,16 +2119,25 @@ class EnvironmentCredential {
1352
2119
  // Copyright (c) Microsoft Corporation.
1353
2120
  // Licensed under the MIT license.
1354
2121
  const DefaultScopeSuffix = "/.default";
1355
- const imdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token";
2122
+ const imdsHost = "http://169.254.169.254";
2123
+ const imdsEndpointPath = "/metadata/identity/oauth2/token";
1356
2124
  const imdsApiVersion = "2018-02-01";
1357
2125
  const azureArcAPIVersion = "2019-11-01";
1358
2126
 
1359
2127
  // Copyright (c) Microsoft Corporation.
2128
+ /**
2129
+ * Most MSIs send requests to the IMDS endpoint, or a similar endpoint. These are GET requests that require sending a `resource` parameter on the query.
2130
+ * This resource can be derived from the scopes received through the getToken call, as long as only one scope is received.
2131
+ * Multiple scopes assume that the resulting token will have access to multiple resources, which won't be the case.
2132
+ *
2133
+ * For that reason, when we encounter multiple scopes, we return undefined.
2134
+ * It's up to the individual MSI implementations to throw the errors (which helps us provide less generic errors).
2135
+ */
1360
2136
  function mapScopesToResource(scopes) {
1361
2137
  let scope = "";
1362
2138
  if (Array.isArray(scopes)) {
1363
2139
  if (scopes.length !== 1) {
1364
- throw new Error("To convert to a resource string the specified array must be exactly length 1");
2140
+ return;
1365
2141
  }
1366
2142
  scope = scopes[0];
1367
2143
  }
@@ -1373,86 +2149,174 @@ function mapScopesToResource(scopes) {
1373
2149
  }
1374
2150
  return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix));
1375
2151
  }
1376
- function msiGenericGetToken(identityClient, requestOptions, expiresInParser, getTokenOptions = {}) {
1377
- return tslib.__awaiter(this, void 0, void 0, function* () {
1378
- const webResource = identityClient.createWebResource(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal, spanOptions: getTokenOptions.tracingOptions && getTokenOptions.tracingOptions.spanOptions, tracingContext: getTokenOptions.tracingOptions && getTokenOptions.tracingOptions.tracingContext }, requestOptions));
1379
- const tokenResponse = yield identityClient.sendTokenRequest(webResource, expiresInParser);
1380
- return (tokenResponse && tokenResponse.accessToken) || null;
1381
- });
2152
+ async function msiGenericGetToken(identityClient, requestOptions, expiresInParser, getTokenOptions = {}) {
2153
+ const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, requestOptions), { allowInsecureConnection: true }));
2154
+ const tokenResponse = await identityClient.sendTokenRequest(request, expiresInParser);
2155
+ return (tokenResponse && tokenResponse.accessToken) || null;
2156
+ }
2157
+
2158
+ // Copyright (c) Microsoft Corporation.
2159
+ const msiName = "ManagedIdentityCredential - AppServiceMSI 2017";
2160
+ const logger$9 = credentialLogger(msiName);
2161
+ function expiresInParser(requestBody) {
2162
+ // Parse a date format like "06/20/2019 02:57:58 +00:00" and
2163
+ // convert it into a JavaScript-formatted date
2164
+ return Date.parse(requestBody.expires_on);
1382
2165
  }
2166
+ function prepareRequestOptions(scopes, clientId) {
2167
+ const resource = mapScopesToResource(scopes);
2168
+ if (!resource) {
2169
+ throw new Error(`${msiName}: Multiple scopes are not supported.`);
2170
+ }
2171
+ const queryParameters = {
2172
+ resource,
2173
+ "api-version": "2017-09-01"
2174
+ };
2175
+ if (clientId) {
2176
+ queryParameters.clientid = clientId;
2177
+ }
2178
+ const query = new URLSearchParams(queryParameters);
2179
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2180
+ if (!process.env.MSI_ENDPOINT) {
2181
+ throw new Error(`${msiName}: Missing environment variable: MSI_ENDPOINT`);
2182
+ }
2183
+ if (!process.env.MSI_SECRET) {
2184
+ throw new Error(`${msiName}: Missing environment variable: MSI_SECRET`);
2185
+ }
2186
+ return {
2187
+ url: `${process.env.MSI_ENDPOINT}?${query.toString()}`,
2188
+ method: "GET",
2189
+ headers: coreRestPipeline.createHttpHeaders({
2190
+ Accept: "application/json",
2191
+ secret: process.env.MSI_SECRET
2192
+ })
2193
+ };
2194
+ }
2195
+ const appServiceMsi2017 = {
2196
+ async isAvailable(scopes) {
2197
+ const resource = mapScopesToResource(scopes);
2198
+ if (!resource) {
2199
+ logger$9.info(`${msiName}: Unavailable. Multiple scopes are not supported.`);
2200
+ return false;
2201
+ }
2202
+ const env = process.env;
2203
+ const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
2204
+ if (!result) {
2205
+ logger$9.info(`${msiName}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`);
2206
+ }
2207
+ return result;
2208
+ },
2209
+ async getToken(configuration, getTokenOptions = {}) {
2210
+ const { identityClient, scopes, clientId } = configuration;
2211
+ logger$9.info(`${msiName}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
2212
+ return msiGenericGetToken(identityClient, prepareRequestOptions(scopes, clientId), expiresInParser, getTokenOptions);
2213
+ }
2214
+ };
1383
2215
 
1384
2216
  // Copyright (c) Microsoft Corporation.
1385
- const logger$6 = credentialLogger("ManagedIdentityCredential - CloudShellMSI");
2217
+ const msiName$1 = "ManagedIdentityCredential - CloudShellMSI";
2218
+ const logger$a = credentialLogger(msiName$1);
1386
2219
  // Cloud Shell MSI doesn't have a special expiresIn parser.
1387
- const expiresInParser = undefined;
1388
- function prepareRequestOptions(resource, clientId) {
2220
+ const expiresInParser$1 = undefined;
2221
+ function prepareRequestOptions$1(scopes, clientId) {
2222
+ const resource = mapScopesToResource(scopes);
2223
+ if (!resource) {
2224
+ throw new Error(`${msiName$1}: Multiple scopes are not supported.`);
2225
+ }
1389
2226
  const body = {
1390
2227
  resource
1391
2228
  };
1392
2229
  if (clientId) {
1393
2230
  body.client_id = clientId;
1394
2231
  }
2232
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2233
+ if (!process.env.MSI_ENDPOINT) {
2234
+ throw new Error(`${msiName$1}: Missing environment variable: MSI_ENDPOINT`);
2235
+ }
2236
+ const params = new URLSearchParams(body);
1395
2237
  return {
1396
2238
  url: process.env.MSI_ENDPOINT,
1397
2239
  method: "POST",
1398
- body: qs.stringify(body),
1399
- headers: {
2240
+ body: params.toString(),
2241
+ headers: coreRestPipeline.createHttpHeaders({
1400
2242
  Accept: "application/json",
1401
- Metadata: true,
2243
+ Metadata: "true",
1402
2244
  "Content-Type": "application/x-www-form-urlencoded"
1403
- }
2245
+ })
1404
2246
  };
1405
2247
  }
1406
2248
  const cloudShellMsi = {
1407
- isAvailable() {
1408
- return tslib.__awaiter(this, void 0, void 0, function* () {
1409
- const result = Boolean(process.env.MSI_ENDPOINT);
1410
- if (!result) {
1411
- logger$6.info("The Azure Cloud Shell MSI is unavailable.");
1412
- }
1413
- return result;
1414
- });
2249
+ async isAvailable(scopes) {
2250
+ const resource = mapScopesToResource(scopes);
2251
+ if (!resource) {
2252
+ logger$a.info(`${msiName$1}: Unavailable. Multiple scopes are not supported.`);
2253
+ return false;
2254
+ }
2255
+ const result = Boolean(process.env.MSI_ENDPOINT);
2256
+ if (!result) {
2257
+ logger$a.info(`${msiName$1}: Unavailable. The environment variable MSI_ENDPOINT is needed.`);
2258
+ }
2259
+ return result;
1415
2260
  },
1416
- getToken(identityClient, resource, clientId, getTokenOptions = {}) {
1417
- return tslib.__awaiter(this, void 0, void 0, function* () {
1418
- logger$6.info(`Using the endpoint coming form the environment variable MSI_ENDPOINT=${process.env.MSI_ENDPOINT}, and using the Cloud Shell to proceed with the authentication.`);
1419
- return msiGenericGetToken(identityClient, prepareRequestOptions(resource, clientId), expiresInParser, getTokenOptions);
1420
- });
2261
+ async getToken(configuration, getTokenOptions = {}) {
2262
+ const { identityClient, scopes, clientId } = configuration;
2263
+ logger$a.info(`${msiName$1}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`);
2264
+ return msiGenericGetToken(identityClient, prepareRequestOptions$1(scopes, clientId), expiresInParser$1, getTokenOptions);
1421
2265
  }
1422
2266
  };
1423
2267
 
1424
2268
  // Copyright (c) Microsoft Corporation.
1425
- const logger$7 = credentialLogger("ManagedIdentityCredential - IMDS");
1426
- function expiresInParser$1(requestBody) {
2269
+ const msiName$2 = "ManagedIdentityCredential - IMDS";
2270
+ const logger$b = credentialLogger(msiName$2);
2271
+ function expiresInParser$2(requestBody) {
1427
2272
  if (requestBody.expires_on) {
1428
2273
  // Use the expires_on timestamp if it's available
1429
2274
  const expires = +requestBody.expires_on * 1000;
1430
- logger$7.info(`IMDS using expires_on: ${expires} (original value: ${requestBody.expires_on})`);
2275
+ logger$b.info(`${msiName$2}: Using expires_on: ${expires} (original value: ${requestBody.expires_on})`);
1431
2276
  return expires;
1432
2277
  }
1433
2278
  else {
1434
2279
  // If these aren't possible, use expires_in and calculate a timestamp
1435
2280
  const expires = Date.now() + requestBody.expires_in * 1000;
1436
- logger$7.info(`IMDS using expires_in: ${expires} (original value: ${requestBody.expires_in})`);
2281
+ logger$b.info(`${msiName$2}: IMDS using expires_in: ${expires} (original value: ${requestBody.expires_in})`);
1437
2282
  return expires;
1438
2283
  }
1439
2284
  }
1440
- function prepareRequestOptions$1(resource, clientId) {
1441
- const queryParameters = {
1442
- resource,
1443
- "api-version": imdsApiVersion
2285
+ function prepareRequestOptions$2(scopes, clientId, options) {
2286
+ var _a;
2287
+ const resource = mapScopesToResource(scopes);
2288
+ if (!resource) {
2289
+ throw new Error(`${msiName$2}: Multiple scopes are not supported.`);
2290
+ }
2291
+ const { skipQuery, skipMetadataHeader } = options || {};
2292
+ let query = "";
2293
+ // Pod Identity will try to process this request even if the Metadata header is missing.
2294
+ // We can exclude the request query to ensure no IMDS endpoint tries to process the ping request.
2295
+ if (!skipQuery) {
2296
+ const queryParameters = {
2297
+ resource,
2298
+ "api-version": imdsApiVersion
2299
+ };
2300
+ if (clientId) {
2301
+ queryParameters.client_id = clientId;
2302
+ }
2303
+ const params = new URLSearchParams(queryParameters);
2304
+ query = `?${params.toString()}`;
2305
+ }
2306
+ const url = new URL(imdsEndpointPath, (_a = process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : imdsHost);
2307
+ const rawHeaders = {
2308
+ Accept: "application/json",
2309
+ Metadata: "true"
1444
2310
  };
1445
- if (clientId) {
1446
- queryParameters.client_id = clientId;
2311
+ // Remove the Metadata header to invoke a request error from some IMDS endpoints.
2312
+ if (skipMetadataHeader) {
2313
+ delete rawHeaders.Metadata;
1447
2314
  }
1448
2315
  return {
1449
- url: imdsEndpoint,
2316
+ // In this case, the `?` should be added in the "query" variable `skipQuery` is not set.
2317
+ url: `${url}${query}`,
1450
2318
  method: "GET",
1451
- queryParameters,
1452
- headers: {
1453
- Accept: "application/json",
1454
- Metadata: true
1455
- }
2319
+ headers: coreRestPipeline.createHttpHeaders(rawHeaders)
1456
2320
  };
1457
2321
  }
1458
2322
  // 800ms -> 1600ms -> 3200ms
@@ -1462,153 +2326,122 @@ const imdsMsiRetryConfig = {
1462
2326
  intervalIncrement: 2
1463
2327
  };
1464
2328
  const imdsMsi = {
1465
- isAvailable(identityClient, resource, clientId, getTokenOptions) {
1466
- var _a, _b, _c;
1467
- return tslib.__awaiter(this, void 0, void 0, function* () {
1468
- const { span, updatedOptions } = createSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions);
1469
- const request = prepareRequestOptions$1(resource, clientId);
1470
- // This will always be populated, but let's make TypeScript happy
1471
- if (request.headers) {
1472
- // Remove the Metadata header to invoke a request error from
1473
- // IMDS endpoint
1474
- delete request.headers.Metadata;
1475
- }
1476
- request.spanOptions = (_a = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _a === void 0 ? void 0 : _a.spanOptions;
1477
- request.tracingContext = (_b = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _b === void 0 ? void 0 : _b.tracingContext;
2329
+ async isAvailable(scopes, identityClient, clientId, getTokenOptions) {
2330
+ var _a, _b;
2331
+ const resource = mapScopesToResource(scopes);
2332
+ if (!resource) {
2333
+ logger$b.info(`${msiName$2}: Unavailable. Multiple scopes are not supported.`);
2334
+ return false;
2335
+ }
2336
+ const { span, updatedOptions: options } = createSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions);
2337
+ // if the PodIdenityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist
2338
+ if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
2339
+ return true;
2340
+ }
2341
+ const requestOptions = prepareRequestOptions$2(resource, clientId, {
2342
+ skipMetadataHeader: true,
2343
+ skipQuery: true
2344
+ });
2345
+ requestOptions.tracingOptions = options.tracingOptions;
2346
+ try {
2347
+ // Create a request with a timeout since we expect that
2348
+ // not having a "Metadata" header should cause an error to be
2349
+ // returned quickly from the endpoint, proving its availability.
2350
+ const request = coreRestPipeline.createPipelineRequest(requestOptions);
2351
+ request.timeout = (_b = (_a = options.requestOptions) === null || _a === void 0 ? void 0 : _a.timeout) !== null && _b !== void 0 ? _b : 300;
2352
+ // This MSI uses the imdsEndpoint to get the token, which only uses http://
2353
+ request.allowInsecureConnection = true;
1478
2354
  try {
1479
- // Create a request with a timeout since we expect that
1480
- // not having a "Metadata" header should cause an error to be
1481
- // returned quickly from the endpoint, proving its availability.
1482
- const webResource = identityClient.createWebResource(request);
1483
- webResource.timeout = ((_c = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.requestOptions) === null || _c === void 0 ? void 0 : _c.timeout) || 500;
1484
- try {
1485
- logger$7.info(`Pinging IMDS endpoint`);
1486
- yield identityClient.sendRequest(webResource);
1487
- }
1488
- catch (err) {
1489
- if ((err.name === "RestError" && err.code === coreHttp.RestError.REQUEST_SEND_ERROR) ||
1490
- err.name === "AbortError" ||
1491
- err.code === "ECONNREFUSED" || // connection refused
1492
- err.code === "EHOSTDOWN" // host is down
1493
- ) {
1494
- // If the request failed, or NodeJS was unable to establish a connection,
1495
- // or the host was down, we'll assume the IMDS endpoint isn't available.
1496
- logger$7.info(`The Azure IMDS endpoint is unavailable`);
1497
- span.setStatus({
1498
- code: coreTracing.SpanStatusCode.ERROR,
1499
- message: err.message
1500
- });
1501
- return false;
1502
- }
1503
- }
1504
- // If we received any response, the endpoint is available
1505
- logger$7.info(`The Azure IMDS endpoint is available`);
1506
- // IMDS MSI available!
1507
- return true;
2355
+ logger$b.info(`${msiName$2}: Pinging the Azure IMDS endpoint`);
2356
+ await identityClient.sendRequest(request);
1508
2357
  }
1509
2358
  catch (err) {
1510
- // createWebResource failed.
1511
- // This error should bubble up to the user.
1512
- logger$7.info(`Error when creating the WebResource for the IMDS endpoint: ${err.message}`);
1513
- span.setStatus({
1514
- code: coreTracing.SpanStatusCode.ERROR,
1515
- message: err.message
1516
- });
1517
- throw err;
1518
- }
1519
- finally {
1520
- span.end();
1521
- }
1522
- });
1523
- },
1524
- getToken(identityClient, resource, clientId, getTokenOptions = {}) {
1525
- return tslib.__awaiter(this, void 0, void 0, function* () {
1526
- logger$7.info(`Using the IMDS endpoint coming form the environment variable MSI_ENDPOINT=${process.env.MSI_ENDPOINT}, and using the cloud shell to proceed with the authentication.`);
1527
- let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;
1528
- for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {
1529
- try {
1530
- return yield msiGenericGetToken(identityClient, prepareRequestOptions$1(resource, clientId), expiresInParser$1, getTokenOptions);
1531
- }
1532
- catch (error) {
1533
- if (error.statusCode === 404) {
1534
- yield coreHttp.delay(nextDelayInMs);
1535
- nextDelayInMs *= imdsMsiRetryConfig.intervalIncrement;
1536
- continue;
1537
- }
1538
- throw error;
2359
+ if ((err.name === "RestError" && err.code === coreRestPipeline.RestError.REQUEST_SEND_ERROR) ||
2360
+ err.name === "AbortError" ||
2361
+ err.code === "ENETUNREACH" || // Network unreachable
2362
+ err.code === "ECONNREFUSED" || // connection refused
2363
+ err.code === "EHOSTDOWN" // host is down
2364
+ ) {
2365
+ // If the request failed, or Node.js was unable to establish a connection,
2366
+ // or the host was down, we'll assume the IMDS endpoint isn't available.
2367
+ logger$b.info(`${msiName$2}: The Azure IMDS endpoint is unavailable`);
2368
+ span.setStatus({
2369
+ code: coreTracing.SpanStatusCode.ERROR,
2370
+ message: err.message
2371
+ });
2372
+ return false;
1539
2373
  }
1540
2374
  }
1541
- throw new AuthenticationError(404, `Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`);
1542
- });
1543
- }
1544
- };
1545
-
1546
- // Copyright (c) Microsoft Corporation.
1547
- const logger$8 = credentialLogger("ManagedIdentityCredential - AppServiceMSI 2017");
1548
- function expiresInParser$2(requestBody) {
1549
- // Parse a date format like "06/20/2019 02:57:58 +00:00" and
1550
- // convert it into a JavaScript-formatted date
1551
- return Date.parse(requestBody.expires_on);
1552
- }
1553
- function prepareRequestOptions$2(resource, clientId) {
1554
- const queryParameters = {
1555
- resource,
1556
- "api-version": "2017-09-01"
1557
- };
1558
- if (clientId) {
1559
- queryParameters.clientid = clientId;
1560
- }
1561
- return {
1562
- url: process.env.MSI_ENDPOINT,
1563
- method: "GET",
1564
- queryParameters,
1565
- headers: {
1566
- Accept: "application/json",
1567
- secret: process.env.MSI_SECRET
2375
+ // If we received any response, the endpoint is available
2376
+ logger$b.info(`${msiName$2}: The Azure IMDS endpoint is available`);
2377
+ return true;
1568
2378
  }
1569
- };
1570
- }
1571
- const appServiceMsi2017 = {
1572
- isAvailable() {
1573
- return tslib.__awaiter(this, void 0, void 0, function* () {
1574
- const env = process.env;
1575
- const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
1576
- if (!result) {
1577
- logger$8.info("The Azure App Service MSI 2017 is unavailable.");
2379
+ catch (err) {
2380
+ // createWebResource failed.
2381
+ // This error should bubble up to the user.
2382
+ logger$b.info(`${msiName$2}: Error when creating the WebResource for the Azure IMDS endpoint: ${err.message}`);
2383
+ span.setStatus({
2384
+ code: coreTracing.SpanStatusCode.ERROR,
2385
+ message: err.message
2386
+ });
2387
+ throw err;
2388
+ }
2389
+ finally {
2390
+ span.end();
2391
+ }
2392
+ },
2393
+ async getToken(configuration, getTokenOptions = {}) {
2394
+ const { identityClient, scopes, clientId } = configuration;
2395
+ logger$b.info(`${msiName$2}: Using the Azure IMDS endpoint coming from the environment variable MSI_ENDPOINT=${process.env.MSI_ENDPOINT}, and using the cloud shell to proceed with the authentication.`);
2396
+ let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;
2397
+ for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {
2398
+ try {
2399
+ return await msiGenericGetToken(identityClient, prepareRequestOptions$2(scopes, clientId), expiresInParser$2, getTokenOptions);
2400
+ }
2401
+ catch (error) {
2402
+ if (error.statusCode === 404) {
2403
+ await coreUtil.delay(nextDelayInMs);
2404
+ nextDelayInMs *= imdsMsiRetryConfig.intervalIncrement;
2405
+ continue;
2406
+ }
2407
+ throw error;
1578
2408
  }
1579
- return result;
1580
- });
1581
- },
1582
- getToken(identityClient, resource, clientId, getTokenOptions = {}) {
1583
- return tslib.__awaiter(this, void 0, void 0, function* () {
1584
- logger$8.info(`Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
1585
- return msiGenericGetToken(identityClient, prepareRequestOptions$2(resource, clientId), expiresInParser$2, getTokenOptions);
1586
- });
2409
+ }
2410
+ throw new AuthenticationError(404, `${msiName$2}: Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`);
1587
2411
  }
1588
2412
  };
1589
2413
 
1590
2414
  // Copyright (c) Microsoft Corporation.
1591
- const logger$9 = credentialLogger("ManagedIdentityCredential - ArcMSI");
2415
+ const msiName$3 = "ManagedIdentityCredential - Azure Arc MSI";
2416
+ const logger$c = credentialLogger(msiName$3);
1592
2417
  // Azure Arc MSI doesn't have a special expiresIn parser.
1593
2418
  const expiresInParser$3 = undefined;
1594
- function prepareRequestOptions$3(resource) {
2419
+ function prepareRequestOptions$3(scopes) {
2420
+ const resource = mapScopesToResource(scopes);
2421
+ if (!resource) {
2422
+ throw new Error(`${msiName$3}: Multiple scopes are not supported.`);
2423
+ }
1595
2424
  const queryParameters = {
1596
2425
  resource,
1597
2426
  "api-version": azureArcAPIVersion
1598
2427
  };
1599
- return {
2428
+ const query = new URLSearchParams(queryParameters);
2429
+ // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
2430
+ if (!process.env.IDENTITY_ENDPOINT) {
2431
+ throw new Error(`${msiName$3}: Missing environment variable: IDENTITY_ENDPOINT`);
2432
+ }
2433
+ return coreRestPipeline.createPipelineRequest({
1600
2434
  // Should be similar to: http://localhost:40342/metadata/identity/oauth2/token
1601
- url: process.env.IDENTITY_ENDPOINT,
2435
+ url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
1602
2436
  method: "GET",
1603
- queryParameters,
1604
- headers: {
2437
+ headers: coreRestPipeline.createHttpHeaders({
1605
2438
  Accept: "application/json",
1606
- Metadata: true
1607
- }
1608
- };
2439
+ Metadata: "true"
2440
+ })
2441
+ });
1609
2442
  }
1610
2443
  // Since "fs"'s readFileSync locks the thread, and to avoid extra dependencies.
1611
- function readFileAsync(path, options) {
2444
+ function readFileAsync$1(path, options) {
1612
2445
  return new Promise((resolve, reject) => fs.readFile(path, options, (err, data) => {
1613
2446
  if (err) {
1614
2447
  reject(err);
@@ -1616,560 +2449,349 @@ function readFileAsync(path, options) {
1616
2449
  resolve(data);
1617
2450
  }));
1618
2451
  }
1619
- function filePathRequest(identityClient, requestPrepareOptions) {
1620
- return tslib.__awaiter(this, void 0, void 0, function* () {
1621
- const response = yield identityClient.sendRequest(identityClient.createWebResource(requestPrepareOptions));
1622
- if (response.status !== 401) {
1623
- let message = "";
1624
- if (response.bodyAsText) {
1625
- message = ` Response: ${response.bodyAsText}`;
1626
- }
1627
- throw new AuthenticationError(response.status, `To authenticate with Azure Arc MSI, status code 401 is expected on the first request.${message}`);
2452
+ async function filePathRequest(identityClient, requestPrepareOptions) {
2453
+ const response = await identityClient.sendRequest(coreRestPipeline.createPipelineRequest(requestPrepareOptions));
2454
+ if (response.status !== 401) {
2455
+ let message = "";
2456
+ if (response.bodyAsText) {
2457
+ message = ` Response: ${response.bodyAsText}`;
1628
2458
  }
1629
- const authHeader = response.headers.get("www-authenticate") || "";
2459
+ throw new AuthenticationError(response.status, `${msiName$3}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`);
2460
+ }
2461
+ const authHeader = response.headers.get("www-authenticate") || "";
2462
+ try {
1630
2463
  return authHeader.split("=").slice(1)[0];
1631
- });
2464
+ }
2465
+ catch (e) {
2466
+ throw Error(`Invalid www-authenticate header format: ${authHeader}`);
2467
+ }
1632
2468
  }
1633
2469
  const arcMsi = {
1634
- isAvailable() {
1635
- return tslib.__awaiter(this, void 0, void 0, function* () {
1636
- const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
1637
- if (!result) {
1638
- logger$9.info("The Azure Arc MSI is unavailable.");
1639
- }
1640
- return result;
1641
- });
2470
+ async isAvailable(scopes) {
2471
+ const resource = mapScopesToResource(scopes);
2472
+ if (!resource) {
2473
+ logger$c.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`);
2474
+ return false;
2475
+ }
2476
+ const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
2477
+ if (!result) {
2478
+ logger$c.info(`${msiName$3}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`);
2479
+ }
2480
+ return result;
1642
2481
  },
1643
- getToken(identityClient, resource, clientId, getTokenOptions = {}) {
1644
- return tslib.__awaiter(this, void 0, void 0, function* () {
1645
- logger$9.info(`Using the Azure Arc MSI to authenticate.`);
1646
- if (clientId) {
1647
- throw new Error("User assigned identity is not supported by the Azure Arc Managed Identity Endpoint. To authenticate with the system assigned identity omit the client id when constructing the ManagedIdentityCredential, or if authenticating with the DefaultAzureCredential ensure the AZURE_CLIENT_ID environment variable is not set.");
1648
- }
1649
- const requestOptions = Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal, spanOptions: getTokenOptions.tracingOptions && getTokenOptions.tracingOptions.spanOptions }, prepareRequestOptions$3(resource));
1650
- const filePath = yield filePathRequest(identityClient, requestOptions);
1651
- if (!filePath) {
1652
- throw new Error("Azure Arc MSI failed to find the token file.");
1653
- }
1654
- const key = yield readFileAsync(filePath, { encoding: "utf-8" });
1655
- requestOptions.headers["Authorization"] = `Basic ${key}`;
1656
- return msiGenericGetToken(identityClient, requestOptions, expiresInParser$3, getTokenOptions);
1657
- });
1658
- }
1659
- };
1660
-
1661
- // Copyright (c) Microsoft Corporation.
1662
- const logger$a = credentialLogger("ManagedIdentityCredential");
1663
- /**
1664
- * Attempts authentication using a managed identity that has been assigned
1665
- * to the deployment environment. This authentication type works in Azure VMs,
1666
- * App Service and Azure Functions applications, and inside of Azure Cloud Shell.
1667
- *
1668
- * More information about configuring managed identities can be found here:
1669
- *
1670
- * https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
1671
- */
1672
- class ManagedIdentityCredential {
1673
- /**
1674
- * @internal
1675
- * @hidden
1676
- */
1677
- constructor(clientIdOrOptions, options) {
1678
- this.isEndpointUnavailable = null;
1679
- if (typeof clientIdOrOptions === "string") {
1680
- // clientId, options constructor
1681
- this.clientId = clientIdOrOptions;
1682
- this.identityClient = new IdentityClient(options);
2482
+ async getToken(configuration, getTokenOptions = {}) {
2483
+ var _a;
2484
+ const { identityClient, scopes, clientId } = configuration;
2485
+ logger$c.info(`${msiName$3}: Authenticating.`);
2486
+ if (clientId) {
2487
+ throw new Error(`${msiName$3}: User assigned identity is not supported by the Azure Arc Managed Identity Endpoint. To authenticate with the system assigned identity, omit the client id when constructing the ManagedIdentityCredential, or if authenticating with the DefaultAzureCredential ensure the AZURE_CLIENT_ID environment variable is not set.`);
1683
2488
  }
1684
- else {
1685
- // options only constructor
1686
- this.identityClient = new IdentityClient(clientIdOrOptions);
2489
+ const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes)), { allowInsecureConnection: true });
2490
+ const filePath = await filePathRequest(identityClient, requestOptions);
2491
+ if (!filePath) {
2492
+ throw new Error(`${msiName$3}: Failed to find the token file.`);
1687
2493
  }
2494
+ const key = await readFileAsync$1(filePath, { encoding: "utf-8" });
2495
+ (_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`);
2496
+ return msiGenericGetToken(identityClient, requestOptions, expiresInParser$3, getTokenOptions);
1688
2497
  }
1689
- cachedAvailableMSI(resource, clientId, getTokenOptions) {
1690
- return tslib.__awaiter(this, void 0, void 0, function* () {
1691
- if (this.cachedMSI) {
1692
- return this.cachedMSI;
1693
- }
1694
- // "fabricMsi" can't be added yet because our HTTPs pipeline doesn't allow skipping the SSL verification step,
1695
- // which is necessary since Service Fabric only provides self-signed certificates on their Identity Endpoint.
1696
- const MSIs = [appServiceMsi2017, cloudShellMsi, arcMsi, imdsMsi];
1697
- for (const msi of MSIs) {
1698
- if (yield msi.isAvailable(this.identityClient, resource, clientId, getTokenOptions)) {
1699
- this.cachedMSI = msi;
1700
- return msi;
1701
- }
1702
- }
1703
- throw new CredentialUnavailableError("ManagedIdentityCredential - No MSI credential available");
1704
- });
1705
- }
1706
- authenticateManagedIdentity(scopes, clientId, getTokenOptions) {
1707
- return tslib.__awaiter(this, void 0, void 0, function* () {
1708
- const resource = mapScopesToResource(scopes);
1709
- const { span, updatedOptions } = createSpan("ManagedIdentityCredential-authenticateManagedIdentity", getTokenOptions);
1710
- try {
1711
- // Determining the available MSI, and avoiding checking for other MSIs while the program is running.
1712
- const availableMSI = yield this.cachedAvailableMSI(resource, clientId, updatedOptions);
1713
- return availableMSI.getToken(this.identityClient, resource, clientId, updatedOptions);
1714
- }
1715
- catch (err) {
1716
- span.setStatus({
1717
- code: coreTracing.SpanStatusCode.ERROR,
1718
- message: err.message
1719
- });
1720
- throw err;
1721
- }
1722
- finally {
1723
- span.end();
1724
- }
1725
- });
1726
- }
1727
- /**
1728
- * Authenticates with Azure Active Directory and returns an access token if
1729
- * successful. If authentication cannot be performed at this time, this method may
1730
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
1731
- * containing failure details will be thrown.
1732
- *
1733
- * @param scopes - The list of scopes for which the token will have access.
1734
- * @param options - The options used to configure any requests this
1735
- * TokenCredential implementation might make.
1736
- */
1737
- getToken(scopes, options) {
1738
- return tslib.__awaiter(this, void 0, void 0, function* () {
1739
- let result = null;
1740
- const { span, updatedOptions } = createSpan("ManagedIdentityCredential-getToken", options);
1741
- try {
1742
- // isEndpointAvailable can be true, false, or null,
1743
- // If it's null, it means we don't yet know whether
1744
- // the endpoint is available and need to check for it.
1745
- if (this.isEndpointUnavailable !== true) {
1746
- result = yield this.authenticateManagedIdentity(scopes, this.clientId, updatedOptions);
1747
- if (result === null) {
1748
- // If authenticateManagedIdentity returns null,
1749
- // it means no MSI endpoints are available.
1750
- // If so, we avoid trying to reach to them in future requests.
1751
- this.isEndpointUnavailable = true;
1752
- // It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method),
1753
- // yet we had no access token. For this reason, we'll throw once with a specific message:
1754
- const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
1755
- logger$a.getToken.info(formatError(scopes, error));
1756
- throw error;
1757
- }
1758
- // Since `authenticateManagedIdentity` didn't throw, and the result was not null,
1759
- // We will assume that this endpoint is reachable from this point forward,
1760
- // and avoid pinging again to it.
1761
- this.isEndpointUnavailable = false;
1762
- }
1763
- else {
1764
- // We've previously determined that the endpoint was unavailable,
1765
- // either because it was unreachable or permanently unable to authenticate.
1766
- const error = new CredentialUnavailableError("The managed identity endpoint is not currently available");
1767
- logger$a.getToken.info(formatError(scopes, error));
1768
- throw error;
1769
- }
1770
- logger$a.getToken.info(formatSuccess(scopes));
1771
- return result;
1772
- }
1773
- catch (err) {
1774
- // CredentialUnavailable errors are expected to reach here.
1775
- // We intend them to bubble up, so that DefaultAzureCredential can catch them.
1776
- if (err.name === "AuthenticationRequiredError") {
1777
- throw err;
1778
- }
1779
- // Expected errors to reach this point:
1780
- // - Errors coming from a method unexpectedly breaking.
1781
- // - When identityClient.sendTokenRequest throws, in which case
1782
- // if the status code was 400, it means that the endpoint is working,
1783
- // but no identity is available.
1784
- span.setStatus({
1785
- code: coreTracing.SpanStatusCode.ERROR,
1786
- message: err.message
1787
- });
1788
- // If either the network is unreachable,
1789
- // we can safely assume the credential is unavailable.
1790
- if (err.code === "ENETUNREACH") {
1791
- const error = new CredentialUnavailableError("ManagedIdentityCredential is unavailable. Network unreachable.");
1792
- logger$a.getToken.info(formatError(scopes, error));
1793
- throw error;
1794
- }
1795
- // If either the host was unreachable,
1796
- // we can safely assume the credential is unavailable.
1797
- if (err.code === "EHOSTUNREACH") {
1798
- const error = new CredentialUnavailableError("ManagedIdentityCredential is unavailable. No managed identity endpoint found.");
1799
- logger$a.getToken.info(formatError(scopes, error));
1800
- throw error;
1801
- }
1802
- // If err.statusCode has a value of 400, it comes from sendTokenRequest,
1803
- // and it means that the endpoint is working, but that no identity is available.
1804
- if (err.statusCode === 400) {
1805
- throw new CredentialUnavailableError("The managed identity endpoint is indicating there's no available identity");
1806
- }
1807
- // If the error has no status code, we can assume there was no available identity.
1808
- // This will throw silently during any ChainedTokenCredential.
1809
- if (err.statusCode === undefined) {
1810
- throw new CredentialUnavailableError(`ManagedIdentityCredential authentication failed. Message ${err.message}`);
1811
- }
1812
- // Any other error should break the chain.
1813
- throw new AuthenticationError(err.statusCode, {
1814
- error: "ManagedIdentityCredential authentication failed.",
1815
- error_description: err.message
1816
- });
1817
- }
1818
- finally {
1819
- // Finally is always called, both if we return and if we throw in the above try/catch.
1820
- span.end();
1821
- }
1822
- });
1823
- }
1824
- }
2498
+ };
1825
2499
 
1826
2500
  // Copyright (c) Microsoft Corporation.
1827
- /**
1828
- * Throws if the received scope is not valid.
1829
- * @internal
1830
- */
1831
- function ensureValidScope(scope, logger) {
1832
- if (!scope.match(/^[0-9a-zA-Z-.:/]+$/)) {
1833
- const error = new Error("Invalid scope was specified by the user or calling client");
1834
- logger.getToken.info(formatError(scope, error));
1835
- throw error;
1836
- }
2501
+ const msiName$4 = "ManagedIdentityCredential - Token Exchange";
2502
+ const logger$d = credentialLogger(msiName$4);
2503
+ const readFileAsync$2 = util.promisify(fs__default.readFile);
2504
+ function expiresInParser$4(requestBody) {
2505
+ // Parses a string representation of the seconds since epoch into a number value
2506
+ return Number(requestBody.expires_on);
1837
2507
  }
1838
- /**
1839
- * Returns the resource out of a scope.
1840
- * @internal
1841
- */
1842
- function getScopeResource(scope) {
1843
- return scope.replace(/\/.default$/, "");
2508
+ function prepareRequestOptions$4(scopes, clientAssertion, clientId) {
2509
+ var _a;
2510
+ const bodyParams = {
2511
+ scope: Array.isArray(scopes) ? scopes.join(" ") : scopes,
2512
+ client_assertion: clientAssertion,
2513
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
2514
+ client_id: clientId,
2515
+ grant_type: "client_credentials"
2516
+ };
2517
+ const urlParams = new URLSearchParams(bodyParams);
2518
+ 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);
2519
+ return {
2520
+ url: url.toString(),
2521
+ method: "POST",
2522
+ body: urlParams.toString(),
2523
+ headers: coreRestPipeline.createHttpHeaders({
2524
+ Accept: "application/json"
2525
+ })
2526
+ };
1844
2527
  }
1845
-
1846
- // Copyright (c) Microsoft Corporation.
1847
- function getSafeWorkingDir() {
1848
- if (process.platform === "win32") {
1849
- if (!process.env.SystemRoot) {
1850
- throw new Error("Azure CLI credential expects a 'SystemRoot' environment variable");
2528
+ function tokenExchangeMsi() {
2529
+ const azureFederatedTokenFilePath = process.env.AZURE_FEDERATED_TOKEN_FILE;
2530
+ let azureFederatedTokenFileContent = undefined;
2531
+ let cacheDate = undefined;
2532
+ // Only reads from the assertion file once every 5 minutes
2533
+ async function readAssertion() {
2534
+ // Cached assertions expire after 5 minutes
2535
+ if (cacheDate !== undefined && Date.now() - cacheDate >= 1000 * 60 * 5) {
2536
+ azureFederatedTokenFileContent = undefined;
1851
2537
  }
1852
- return process.env.SystemRoot;
1853
- }
1854
- else {
1855
- return "/bin";
1856
- }
1857
- }
1858
- const logger$b = credentialLogger("AzureCliCredential");
1859
- /**
1860
- * This credential will use the currently logged-in user login information
1861
- * via the Azure CLI ('az') commandline tool.
1862
- * To do so, it will read the user access token and expire time
1863
- * with Azure CLI command "az account get-access-token".
1864
- * To be able to use this credential, ensure that you have already logged
1865
- * in via the 'az' tool using the command "az login" from the commandline.
1866
- */
1867
- class AzureCliCredential {
1868
- /**
1869
- * Gets the access token from Azure CLI
1870
- * @param resource - The resource to use when getting the token
1871
- */
1872
- getAzureCliAccessToken(resource) {
1873
- return tslib.__awaiter(this, void 0, void 0, function* () {
1874
- return new Promise((resolve, reject) => {
1875
- try {
1876
- child_process.execFile("az", ["account", "get-access-token", "--output", "json", "--resource", resource], { cwd: getSafeWorkingDir() }, (error, stdout, stderr) => {
1877
- resolve({ stdout: stdout, stderr: stderr, error });
1878
- });
1879
- }
1880
- catch (err) {
1881
- reject(err);
1882
- }
1883
- });
1884
- });
1885
- }
1886
- /**
1887
- * Authenticates with Azure Active Directory and returns an access token if
1888
- * successful. If authentication cannot be performed at this time, this method may
1889
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
1890
- * containing failure details will be thrown.
1891
- *
1892
- * @param scopes - The list of scopes for which the token will have access.
1893
- * @param options - The options used to configure any requests this
1894
- * TokenCredential implementation might make.
1895
- */
1896
- getToken(scopes, options) {
1897
- return tslib.__awaiter(this, void 0, void 0, function* () {
1898
- return new Promise((resolve, reject) => {
1899
- const scope = typeof scopes === "string" ? scopes : scopes[0];
1900
- logger$b.getToken.info(`Using the scope ${scope}`);
1901
- ensureValidScope(scope, logger$b);
1902
- const resource = getScopeResource(scope);
1903
- let responseData = "";
1904
- const { span } = createSpan("AzureCliCredential-getToken", options);
1905
- this.getAzureCliAccessToken(resource)
1906
- .then((obj) => {
1907
- if (obj.stderr) {
1908
- const isLoginError = obj.stderr.match("(.*)az login(.*)");
1909
- const isNotInstallError = obj.stderr.match("az:(.*)not found") ||
1910
- obj.stderr.startsWith("'az' is not recognized");
1911
- if (isNotInstallError) {
1912
- 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'.");
1913
- logger$b.getToken.info(formatError(scopes, error));
1914
- throw error;
1915
- }
1916
- else if (isLoginError) {
1917
- const error = new CredentialUnavailableError("Please run 'az login' from a command prompt to authenticate before using this credential.");
1918
- logger$b.getToken.info(formatError(scopes, error));
1919
- throw error;
1920
- }
1921
- const error = new CredentialUnavailableError(obj.stderr);
1922
- logger$b.getToken.info(formatError(scopes, error));
1923
- throw error;
1924
- }
1925
- else {
1926
- responseData = obj.stdout;
1927
- const response = JSON.parse(responseData);
1928
- logger$b.getToken.info(formatSuccess(scopes));
1929
- const returnValue = {
1930
- token: response.accessToken,
1931
- expiresOnTimestamp: new Date(response.expiresOn).getTime()
1932
- };
1933
- resolve(returnValue);
1934
- return returnValue;
1935
- }
1936
- })
1937
- .catch((err) => {
1938
- span.setStatus({
1939
- code: coreTracing.SpanStatusCode.ERROR,
1940
- message: err.message
1941
- });
1942
- logger$b.getToken.info(formatError(scopes, err));
1943
- reject(err);
1944
- });
1945
- });
1946
- });
1947
- }
1948
- }
1949
-
1950
- // Copyright (c) Microsoft Corporation.
1951
- /**
1952
- * Easy to mock childProcess utils.
1953
- * @internal
1954
- */
1955
- const processUtils = {
1956
- /**
1957
- * Promisifying childProcess.execFile
1958
- * @internal
1959
- */
1960
- execFile(file, params, options) {
1961
- return new Promise((resolve, reject) => {
1962
- child_process.execFile(file, params, options, (error, stdout, stderr) => {
1963
- if (Buffer.isBuffer(stdout)) {
1964
- stdout = stdout.toString("utf8");
1965
- }
1966
- if (Buffer.isBuffer(stderr)) {
1967
- stderr = stderr.toString("utf8");
1968
- }
1969
- if (stderr || error) {
1970
- reject(stderr ? new Error(stderr) : error);
1971
- }
1972
- else {
1973
- resolve(stdout);
1974
- }
1975
- });
1976
- });
1977
- }
1978
- };
1979
-
1980
- // Copyright (c) Microsoft Corporation.
1981
- const logger$c = credentialLogger("AzurePowerShellCredential");
1982
- const isWindows = process.platform === "win32";
1983
- /**
1984
- * Returns a platform-appropriate command name by appending ".exe" on Windows.
1985
- *
1986
- * @internal
1987
- */
1988
- function formatCommand(commandName) {
1989
- if (isWindows) {
1990
- return `${commandName}.exe`;
1991
- }
1992
- else {
1993
- return commandName;
1994
- }
1995
- }
1996
- /**
1997
- * Receives a list of commands to run, executes them, then returns the outputs.
1998
- * If anything fails, an error is thrown.
1999
- * @internal
2000
- */
2001
- function runCommands(commands) {
2002
- return tslib.__awaiter(this, void 0, void 0, function* () {
2003
- const results = [];
2004
- for (const command of commands) {
2005
- const [file, ...parameters] = command;
2006
- const result = (yield processUtils.execFile(file, parameters, { encoding: "utf8" }));
2007
- results.push(result);
2538
+ if (!azureFederatedTokenFileContent) {
2539
+ const file = await readFileAsync$2(azureFederatedTokenFilePath, "utf8");
2540
+ const value = file.trim();
2541
+ if (!value) {
2542
+ throw new Error(`No content on the file ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
2543
+ }
2544
+ else {
2545
+ azureFederatedTokenFileContent = value;
2546
+ cacheDate = Date.now();
2547
+ }
2008
2548
  }
2009
- return results;
2010
- });
2011
- }
2012
- /**
2013
- * Known PowerShell errors
2014
- * @internal
2015
- */
2016
- const powerShellErrors = {
2017
- login: "Run Connect-AzAccount to login",
2018
- 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"
2019
- };
2020
- /**
2021
- * Messages to use when throwing in this credential.
2022
- * @internal
2023
- */
2024
- const powerShellPublicErrorMessages = {
2025
- login: "Please run 'Connect-AzAccount' from PowerShell to authenticate before using this credential.",
2026
- 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".`
2027
- };
2028
- // PowerShell Azure User not logged in error check.
2029
- const isLoginError = (err) => err.message.match(`(.*)${powerShellErrors.login}(.*)`);
2030
- // Az Module not Installed in Azure PowerShell check.
2031
- const isNotInstalledError = (err) => err.message.match(powerShellErrors.installed);
2032
- /**
2033
- * The PowerShell commands to be tried, in order.
2034
- *
2035
- * @internal
2036
- */
2037
- const commandStack = [formatCommand("pwsh")];
2038
- if (isWindows) {
2039
- commandStack.push(formatCommand("powershell"));
2549
+ return azureFederatedTokenFileContent;
2550
+ }
2551
+ return {
2552
+ async isAvailable(_scopes, _identityClient, clientId) {
2553
+ const env = process.env;
2554
+ const result = Boolean((clientId || env.AZURE_CLIENT_ID) && env.AZURE_TENANT_ID && azureFederatedTokenFilePath);
2555
+ if (!result) {
2556
+ logger$d.info(`${msiName$4}: 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`);
2557
+ }
2558
+ return result;
2559
+ },
2560
+ async getToken(configuration, getTokenOptions = {}) {
2561
+ const { identityClient, scopes, clientId } = configuration;
2562
+ logger$d.info(`${msiName$4}: Using the client assertion coming from environment variables.`);
2563
+ let assertion;
2564
+ try {
2565
+ assertion = await readAssertion();
2566
+ }
2567
+ catch (err) {
2568
+ throw new Error(`${msiName$4}: Failed to read ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
2569
+ }
2570
+ return msiGenericGetToken(identityClient, prepareRequestOptions$4(scopes, assertion, clientId || process.env.AZURE_CLIENT_ID), expiresInParser$4, getTokenOptions);
2571
+ }
2572
+ };
2040
2573
  }
2574
+
2575
+ // Copyright (c) Microsoft Corporation.
2576
+ const logger$e = credentialLogger("ManagedIdentityCredential");
2041
2577
  /**
2042
- * This credential will use the currently logged-in user information from the
2043
- * Azure PowerShell module. To do so, it will read the user access token and
2044
- * expire time with Azure PowerShell command `Get-AzAccessToken -ResourceUrl {ResourceScope}`
2578
+ * Attempts authentication using a managed identity that has been assigned
2579
+ * to the deployment environment. This authentication type works in Azure VMs,
2580
+ * App Service and Azure Functions applications, and inside of Azure Cloud Shell.
2581
+ *
2582
+ * More information about configuring managed identities can be found here:
2045
2583
  *
2046
- * To be able to use this credential:
2047
- * - Install the Azure Az PowerShell module with:
2048
- * `Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force`.
2049
- * - You have already logged in to Azure PowerShell using the command
2050
- * `Connect-AzAccount` from the command line.
2584
+ * https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
2051
2585
  */
2052
- class AzurePowerShellCredential {
2586
+ class ManagedIdentityCredential {
2053
2587
  /**
2054
- * Gets the access token from Azure PowerShell
2055
- * @param resource - The resource to use when getting the token
2588
+ * @internal
2589
+ * @hidden
2056
2590
  */
2057
- getAzurePowerShellAccessToken(resource) {
2058
- return tslib.__awaiter(this, void 0, void 0, function* () {
2059
- // Clone the stack to avoid mutating it while iterating
2060
- for (const powerShellCommand of [...commandStack]) {
2061
- try {
2062
- yield runCommands([[powerShellCommand, "/?"]]);
2063
- }
2064
- catch (e) {
2065
- // Remove this credential from the original stack so that we don't try it again.
2066
- commandStack.shift();
2067
- continue;
2068
- }
2069
- const results = yield runCommands([
2070
- [
2071
- powerShellCommand,
2072
- "-Command",
2073
- "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru"
2074
- ],
2075
- [
2076
- powerShellCommand,
2077
- "-Command",
2078
- `Get-AzAccessToken -ResourceUrl "${resource}" | ConvertTo-Json`
2079
- ]
2080
- ]);
2081
- const result = results[1];
2082
- try {
2083
- return JSON.parse(result);
2084
- }
2085
- catch (e) {
2086
- throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
2087
- }
2591
+ constructor(clientIdOrOptions, options) {
2592
+ this.isEndpointUnavailable = null;
2593
+ if (typeof clientIdOrOptions === "string") {
2594
+ // clientId, options constructor
2595
+ this.clientId = clientIdOrOptions;
2596
+ this.identityClient = new IdentityClient(options);
2597
+ }
2598
+ else {
2599
+ // options only constructor
2600
+ this.identityClient = new IdentityClient(clientIdOrOptions);
2601
+ }
2602
+ }
2603
+ async cachedAvailableMSI(scopes, clientId, getTokenOptions) {
2604
+ if (this.cachedMSI) {
2605
+ return this.cachedMSI;
2606
+ }
2607
+ // "fabricMsi" can't be added yet because our HTTPs pipeline doesn't allow skipping the SSL verification step,
2608
+ // which is necessary since Service Fabric only provides self-signed certificates on their Identity Endpoint.
2609
+ const MSIs = [appServiceMsi2017, cloudShellMsi, arcMsi, tokenExchangeMsi(), imdsMsi];
2610
+ for (const msi of MSIs) {
2611
+ if (await msi.isAvailable(scopes, this.identityClient, clientId, getTokenOptions)) {
2612
+ this.cachedMSI = msi;
2613
+ return msi;
2088
2614
  }
2089
- throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system.`);
2090
- });
2615
+ }
2616
+ throw new CredentialUnavailableError("ManagedIdentityCredential - No MSI credential available");
2617
+ }
2618
+ async authenticateManagedIdentity(scopes, clientId, getTokenOptions) {
2619
+ const { span, updatedOptions } = createSpan("ManagedIdentityCredential-authenticateManagedIdentity", getTokenOptions);
2620
+ try {
2621
+ // Determining the available MSI, and avoiding checking for other MSIs while the program is running.
2622
+ const availableMSI = await this.cachedAvailableMSI(scopes, clientId, updatedOptions);
2623
+ return availableMSI.getToken({
2624
+ identityClient: this.identityClient,
2625
+ scopes,
2626
+ clientId
2627
+ }, updatedOptions);
2628
+ }
2629
+ catch (err) {
2630
+ span.setStatus({
2631
+ code: coreTracing.SpanStatusCode.ERROR,
2632
+ message: err.message
2633
+ });
2634
+ throw err;
2635
+ }
2636
+ finally {
2637
+ span.end();
2638
+ }
2091
2639
  }
2092
2640
  /**
2093
- * Authenticates with Azure Active Directory and returns an access token if
2094
- * successful. If authentication cannot be performed at this time, this method may
2095
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2096
- * containing failure details will be thrown.
2641
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2642
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2643
+ * If an unexpected error occurs, an {@link AuthenticationError} will be thrown with the details of the failure.
2097
2644
  *
2098
2645
  * @param scopes - The list of scopes for which the token will have access.
2099
- * @param options - The options used to configure any requests this TokenCredential implementation might make.
2646
+ * @param options - The options used to configure any requests this
2647
+ * TokenCredential implementation might make.
2100
2648
  */
2101
- getToken(scopes, options = {}) {
2102
- return tslib.__awaiter(this, void 0, void 0, function* () {
2103
- return trace(`${this.constructor.name}.getToken`, options, () => tslib.__awaiter(this, void 0, void 0, function* () {
2104
- const scope = typeof scopes === "string" ? scopes : scopes[0];
2105
- logger$c.getToken.info(`Using the scope ${scope}`);
2106
- ensureValidScope(scope, logger$c);
2107
- const resource = getScopeResource(scope);
2108
- try {
2109
- const response = yield this.getAzurePowerShellAccessToken(resource);
2110
- logger$c.getToken.info(formatSuccess(scopes));
2111
- return {
2112
- token: response.Token,
2113
- expiresOnTimestamp: new Date(response.ExpiresOn).getTime()
2114
- };
2115
- }
2116
- catch (err) {
2117
- if (isNotInstalledError(err)) {
2118
- const error = new CredentialUnavailableError(powerShellPublicErrorMessages.installed);
2119
- logger$c.getToken.info(formatError(scope, error));
2120
- throw error;
2121
- }
2122
- else if (isLoginError(err)) {
2123
- const error = new CredentialUnavailableError(powerShellPublicErrorMessages.login);
2124
- logger$c.getToken.info(formatError(scope, error));
2125
- throw error;
2126
- }
2127
- const error = new CredentialUnavailableError(err);
2128
- logger$c.getToken.info(formatError(scope, error));
2649
+ async getToken(scopes, options) {
2650
+ let result = null;
2651
+ const { span, updatedOptions } = createSpan("ManagedIdentityCredential.getToken", options);
2652
+ try {
2653
+ // isEndpointAvailable can be true, false, or null,
2654
+ // If it's null, it means we don't yet know whether
2655
+ // the endpoint is available and need to check for it.
2656
+ if (this.isEndpointUnavailable !== true) {
2657
+ result = await this.authenticateManagedIdentity(scopes, this.clientId, updatedOptions);
2658
+ if (result === null) {
2659
+ // If authenticateManagedIdentity returns null,
2660
+ // it means no MSI endpoints are available.
2661
+ // If so, we avoid trying to reach to them in future requests.
2662
+ this.isEndpointUnavailable = true;
2663
+ // It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method),
2664
+ // yet we had no access token. For this reason, we'll throw once with a specific message:
2665
+ const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
2666
+ logger$e.getToken.info(formatError(scopes, error));
2129
2667
  throw error;
2130
2668
  }
2131
- }));
2132
- });
2669
+ // Since `authenticateManagedIdentity` didn't throw, and the result was not null,
2670
+ // We will assume that this endpoint is reachable from this point forward,
2671
+ // and avoid pinging again to it.
2672
+ this.isEndpointUnavailable = false;
2673
+ }
2674
+ else {
2675
+ // We've previously determined that the endpoint was unavailable,
2676
+ // either because it was unreachable or permanently unable to authenticate.
2677
+ const error = new CredentialUnavailableError("The managed identity endpoint is not currently available");
2678
+ logger$e.getToken.info(formatError(scopes, error));
2679
+ throw error;
2680
+ }
2681
+ logger$e.getToken.info(formatSuccess(scopes));
2682
+ return result;
2683
+ }
2684
+ catch (err) {
2685
+ // CredentialUnavailable errors are expected to reach here.
2686
+ // We intend them to bubble up, so that DefaultAzureCredential can catch them.
2687
+ if (err.name === "AuthenticationRequiredError") {
2688
+ throw err;
2689
+ }
2690
+ // Expected errors to reach this point:
2691
+ // - Errors coming from a method unexpectedly breaking.
2692
+ // - When identityClient.sendTokenRequest throws, in which case
2693
+ // if the status code was 400, it means that the endpoint is working,
2694
+ // but no identity is available.
2695
+ span.setStatus({
2696
+ code: coreTracing.SpanStatusCode.ERROR,
2697
+ message: err.message
2698
+ });
2699
+ // If either the network is unreachable,
2700
+ // we can safely assume the credential is unavailable.
2701
+ if (err.code === "ENETUNREACH") {
2702
+ const error = new CredentialUnavailableError(`ManagedIdentityCredential is unavailable. Network unreachable. Message: ${err.message}`);
2703
+ logger$e.getToken.info(formatError(scopes, error));
2704
+ throw error;
2705
+ }
2706
+ // If either the host was unreachable,
2707
+ // we can safely assume the credential is unavailable.
2708
+ if (err.code === "EHOSTUNREACH") {
2709
+ const error = new CredentialUnavailableError(`ManagedIdentityCredential is unavailable. No managed identity endpoint found. Message: ${err.message}`);
2710
+ logger$e.getToken.info(formatError(scopes, error));
2711
+ throw error;
2712
+ }
2713
+ // If err.statusCode has a value of 400, it comes from sendTokenRequest,
2714
+ // and it means that the endpoint is working, but that no identity is available.
2715
+ if (err.statusCode === 400) {
2716
+ throw new CredentialUnavailableError(`ManagedIdentityCredential: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`);
2717
+ }
2718
+ // If the error has no status code, we can assume there was no available identity.
2719
+ // This will throw silently during any ChainedTokenCredential.
2720
+ if (err.statusCode === undefined) {
2721
+ throw new CredentialUnavailableError(`ManagedIdentityCredential authentication failed. Message ${err.message}`);
2722
+ }
2723
+ // Any other error should break the chain.
2724
+ throw new AuthenticationError(err.statusCode, {
2725
+ error: "ManagedIdentityCredential authentication failed.",
2726
+ error_description: err.message
2727
+ });
2728
+ }
2729
+ finally {
2730
+ // Finally is always called, both if we return and if we throw in the above try/catch.
2731
+ span.end();
2732
+ }
2133
2733
  }
2134
2734
  }
2135
2735
 
2136
2736
  // Copyright (c) Microsoft Corporation.
2137
2737
  /**
2138
- * Provides a default {@link ChainedTokenCredential} configuration that should work for most applications that use the Azure SDK.
2139
- * The following credential types will be tried, in order:
2738
+ * A shim around ManagedIdentityCredential that adapts it to accept
2739
+ * `DefaultAzureCredentialOptions`.
2140
2740
  *
2141
- * - {@link EnvironmentCredential}
2142
- * - {@link ManagedIdentityCredential}
2143
- * - {@link AzureCliCredential}
2144
- * - {@link AzurePowerShellCredential}
2145
- *
2146
- * Consult the documentation of these credential types for more information
2147
- * on how they attempt authentication.
2741
+ * @internal
2742
+ */
2743
+ class DefaultManagedIdentityCredential extends ManagedIdentityCredential {
2744
+ constructor(options) {
2745
+ var _a;
2746
+ const managedIdentityClientId = (_a = options === null || options === void 0 ? void 0 : options.managedIdentityClientId) !== null && _a !== void 0 ? _a : process.env.AZURE_CLIENT_ID;
2747
+ if (managedIdentityClientId !== undefined) {
2748
+ super(managedIdentityClientId, options);
2749
+ }
2750
+ else {
2751
+ super(options);
2752
+ }
2753
+ }
2754
+ }
2755
+ const defaultCredentials = [
2756
+ EnvironmentCredential,
2757
+ DefaultManagedIdentityCredential,
2758
+ VisualStudioCodeCredential,
2759
+ AzureCliCredential,
2760
+ AzurePowerShellCredential
2761
+ ];
2762
+ /**
2763
+ * Provides a default {@link ChainedTokenCredential} configuration that should
2764
+ * work for most applications that use the Azure SDK.
2148
2765
  */
2149
2766
  class DefaultAzureCredential extends ChainedTokenCredential {
2150
2767
  /**
2151
2768
  * Creates an instance of the DefaultAzureCredential class.
2152
2769
  *
2770
+ * This credential provides a default {@link ChainedTokenCredential} configuration that should
2771
+ * work for most applications that use the Azure SDK.
2772
+ *
2773
+ * The following credential types will be tried, in order:
2774
+ *
2775
+ * - {@link EnvironmentCredential}
2776
+ * - {@link ManagedIdentityCredential}
2777
+ * - {@link VisualStudioCodeCredential}
2778
+ * - {@link AzureCliCredential}
2779
+ * - {@link AzurePowerShellCredential}
2780
+ *
2781
+ * Consult the documentation of these credential types for more information
2782
+ * on how they attempt authentication.
2783
+ *
2784
+ * **Note**: `VisualStudioCodeCredential` is provided by a plugin package:
2785
+ * `@azure/identity-vscode`. If this package is not installed and registered
2786
+ * using the plugin API (`useIdentityPlugin`), then authentication using
2787
+ * `VisualStudioCodeCredential` will not be available.
2788
+ *
2153
2789
  * @param options - Optional parameters. See {@link DefaultAzureCredentialOptions}.
2154
2790
  */
2155
2791
  constructor(options) {
2156
- const credentials = [];
2157
- credentials.push(new EnvironmentCredential(options));
2158
- // A client ID for the ManagedIdentityCredential
2159
- // can be provided either through the optional parameters or through the environment variables.
2160
- const managedIdentityClientId = (options === null || options === void 0 ? void 0 : options.managedIdentityClientId) || process.env.AZURE_CLIENT_ID;
2161
- // If a client ID is not provided, we will try with the system assigned ID.
2162
- if (managedIdentityClientId) {
2163
- credentials.push(new ManagedIdentityCredential(managedIdentityClientId, options));
2164
- }
2165
- else {
2166
- credentials.push(new ManagedIdentityCredential(options));
2167
- }
2168
- credentials.push(new AzureCliCredential());
2169
- credentials.push(new AzurePowerShellCredential());
2170
- super(...credentials);
2792
+ super(...defaultCredentials.map((ctor) => new ctor(options)));
2171
2793
  this.UnavailableMessage =
2172
- "DefaultAzureCredential => failed to retrieve a token from the included credentials";
2794
+ "DefaultAzureCredential => failed to retrieve a token from the included credentials. To troubleshoot, visit https://aka.ms/azsdk/js/identity/defaultazurecredential/troubleshoot.";
2173
2795
  }
2174
2796
  }
2175
2797
 
@@ -2189,8 +2811,9 @@ const interactiveBrowserMockable = {
2189
2811
  class MsalOpenBrowser extends MsalNode {
2190
2812
  constructor(options) {
2191
2813
  super(options);
2192
- this.logger = credentialLogger("NodeJS MSAL Open Browser");
2814
+ this.logger = credentialLogger("Node.js MSAL Open Browser");
2193
2815
  this.redirectUri = options.redirectUri;
2816
+ this.loginHint = options.loginHint;
2194
2817
  const url = new URL(this.redirectUri);
2195
2818
  this.port = parseInt(url.port);
2196
2819
  if (isNaN(this.port)) {
@@ -2198,15 +2821,14 @@ class MsalOpenBrowser extends MsalNode {
2198
2821
  }
2199
2822
  this.hostname = url.hostname;
2200
2823
  }
2201
- acquireTokenByCode(request) {
2202
- return tslib.__awaiter(this, void 0, void 0, function* () {
2203
- return this.publicApp.acquireTokenByCode(request);
2204
- });
2824
+ async acquireTokenByCode(request) {
2825
+ return this.publicApp.acquireTokenByCode(request);
2205
2826
  }
2206
2827
  doGetToken(scopes, options) {
2207
2828
  return new Promise((resolve, reject) => {
2208
2829
  const socketToDestroy = [];
2209
2830
  const requestListener = (req, res) => {
2831
+ var _a;
2210
2832
  if (!req.url) {
2211
2833
  reject(new Error(`Interactive Browser Authentication Error "Did not receive token with a valid expiration"`));
2212
2834
  return;
@@ -2222,7 +2844,9 @@ class MsalOpenBrowser extends MsalNode {
2222
2844
  const tokenRequest = {
2223
2845
  code: url.searchParams.get("code"),
2224
2846
  redirectUri: this.redirectUri,
2225
- scopes: scopes
2847
+ scopes: scopes,
2848
+ authority: options === null || options === void 0 ? void 0 : options.authority,
2849
+ codeVerifier: (_a = this.pkceCodes) === null || _a === void 0 ? void 0 : _a.verifier
2226
2850
  };
2227
2851
  this.acquireTokenByCode(tokenRequest)
2228
2852
  .then((authResponse) => {
@@ -2260,13 +2884,8 @@ class MsalOpenBrowser extends MsalNode {
2260
2884
  });
2261
2885
  };
2262
2886
  const app = http.createServer(requestListener);
2263
- const listen = app.listen(this.port, this.hostname, () => this.logger.info(`InteractiveBrowserCredential listening on port ${this.port}!`));
2264
- app.on("connection", (socket) => socketToDestroy.push(socket));
2265
2887
  const server = stoppable(app);
2266
- this.openAuthCodeUrl(scopes).catch((e) => {
2267
- cleanup();
2268
- reject(e);
2269
- });
2888
+ const listen = app.listen(this.port, this.hostname, () => this.logger.info(`InteractiveBrowserCredential listening on port ${this.port}!`));
2270
2889
  function cleanup() {
2271
2890
  if (listen) {
2272
2891
  listen.close();
@@ -2279,64 +2898,76 @@ class MsalOpenBrowser extends MsalNode {
2279
2898
  server.stop();
2280
2899
  }
2281
2900
  }
2282
- const abortSignal = options === null || options === void 0 ? void 0 : options.abortSignal;
2283
- if (abortSignal) {
2284
- abortSignal.addEventListener("abort", () => {
2901
+ app.on("connection", (socket) => socketToDestroy.push(socket));
2902
+ app.on("listening", () => {
2903
+ const openPromise = this.openAuthCodeUrl(scopes, options);
2904
+ const abortSignal = options === null || options === void 0 ? void 0 : options.abortSignal;
2905
+ if (abortSignal) {
2906
+ abortSignal.addEventListener("abort", () => {
2907
+ cleanup();
2908
+ reject(new Error("Aborted"));
2909
+ });
2910
+ }
2911
+ openPromise.then().catch((e) => {
2285
2912
  cleanup();
2286
- reject(new Error("Aborted"));
2913
+ reject(e);
2287
2914
  });
2288
- }
2915
+ });
2289
2916
  });
2290
2917
  }
2291
- openAuthCodeUrl(scopeArray) {
2292
- return tslib.__awaiter(this, void 0, void 0, function* () {
2293
- const authCodeUrlParameters = {
2294
- scopes: scopeArray,
2295
- redirectUri: this.redirectUri
2296
- };
2297
- const response = yield this.publicApp.getAuthCodeUrl(authCodeUrlParameters);
2298
- try {
2299
- yield interactiveBrowserMockable.open(response, { wait: true });
2300
- }
2301
- catch (e) {
2302
- throw new CredentialUnavailableError(`Could not open a browser window. Error: ${e.message}`);
2303
- }
2304
- });
2918
+ async openAuthCodeUrl(scopeArray, options) {
2919
+ // Initialize CryptoProvider instance
2920
+ const cryptoProvider = new msalNode.CryptoProvider();
2921
+ // Generate PKCE Codes before starting the authorization flow
2922
+ this.pkceCodes = await cryptoProvider.generatePkceCodes();
2923
+ const authCodeUrlParameters = {
2924
+ scopes: scopeArray,
2925
+ redirectUri: this.redirectUri,
2926
+ authority: options === null || options === void 0 ? void 0 : options.authority,
2927
+ loginHint: this.loginHint,
2928
+ codeChallenge: this.pkceCodes.challenge,
2929
+ codeChallengeMethod: "S256" // Use SHA256 Algorithm
2930
+ };
2931
+ const response = await this.publicApp.getAuthCodeUrl(authCodeUrlParameters);
2932
+ try {
2933
+ await interactiveBrowserMockable.open(response, { wait: true });
2934
+ }
2935
+ catch (e) {
2936
+ throw new CredentialUnavailableError(`Could not open a browser window. Error: ${e.message}`);
2937
+ }
2305
2938
  }
2306
2939
  }
2307
2940
 
2308
2941
  // Copyright (c) Microsoft Corporation.
2309
- const logger$d = credentialLogger("InteractiveBrowserCredential");
2942
+ const logger$f = credentialLogger("InteractiveBrowserCredential");
2310
2943
  /**
2311
2944
  * Enables authentication to Azure Active Directory inside of the web browser
2312
2945
  * using the interactive login flow.
2313
- *
2314
- * This credential uses the [Authorization Code Flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow).
2315
- * On NodeJS, it will open a browser window while it listens for a redirect response from the authentication service.
2316
- * On browsers, it authenticates via popups. The `loginStyle` optional parameter can be set to `redirect` to authenticate by redirecting the user to an Azure secure login page, which then will redirect the user back to the web application where the authentication started.
2317
- *
2318
- * For Node.js, if a `clientId` is provided, the Azure Active Directory application will need to be configured to have a "Mobile and desktop applications" redirect endpoint.
2319
- * Follow our guide on [setting up Redirect URIs for Desktop apps that calls to web APIs](https://docs.microsoft.com/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris).
2320
2946
  */
2321
2947
  class InteractiveBrowserCredential {
2322
2948
  /**
2323
2949
  * Creates an instance of InteractiveBrowserCredential with the details needed.
2324
2950
  *
2951
+ * This credential uses the [Authorization Code Flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow).
2952
+ * On Node.js, it will open a browser window while it listens for a redirect response from the authentication service.
2953
+ * On browsers, it authenticates via popups. The `loginStyle` optional parameter can be set to `redirect` to authenticate by redirecting the user to an Azure secure login page, which then will redirect the user back to the web application where the authentication started.
2954
+ *
2955
+ * For Node.js, if a `clientId` is provided, the Azure Active Directory application will need to be configured to have a "Mobile and desktop applications" redirect endpoint.
2956
+ * Follow our guide on [setting up Redirect URIs for Desktop apps that calls to web APIs](https://docs.microsoft.com/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris).
2957
+ *
2325
2958
  * @param options - Options for configuring the client which makes the authentication requests.
2326
2959
  */
2327
2960
  constructor(options = {}) {
2328
2961
  const redirectUri = typeof options.redirectUri === "function"
2329
2962
  ? options.redirectUri()
2330
2963
  : options.redirectUri || "http://localhost";
2331
- this.msalFlow = new MsalOpenBrowser(Object.assign(Object.assign({}, options), { tokenCredentialOptions: options, logger: logger$d,
2964
+ this.msalFlow = new MsalOpenBrowser(Object.assign(Object.assign({}, options), { tokenCredentialOptions: options, logger: logger$f,
2332
2965
  redirectUri }));
2333
2966
  this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
2334
2967
  }
2335
2968
  /**
2336
- * Authenticates with Azure Active Directory and returns an access token if
2337
- * successful. If authentication cannot be performed at this time, this method may
2338
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2339
- * containing failure details will be thrown.
2969
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2970
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2340
2971
  *
2341
2972
  * If the user provided the option `disableAutomaticAuthentication`,
2342
2973
  * once the token can't be retrieved silently,
@@ -2346,33 +2977,30 @@ class InteractiveBrowserCredential {
2346
2977
  * @param options - The options used to configure any requests this
2347
2978
  * TokenCredential implementation might make.
2348
2979
  */
2349
- getToken(scopes, options = {}) {
2350
- return tslib.__awaiter(this, void 0, void 0, function* () {
2351
- return trace(`${this.constructor.name}.getToken`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
2352
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2353
- return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
2354
- }));
2980
+ async getToken(scopes, options = {}) {
2981
+ return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
2982
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2983
+ return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
2355
2984
  });
2356
2985
  }
2357
2986
  /**
2358
- * Authenticates with Azure Active Directory and returns an access token if
2359
- * successful. If authentication cannot be performed at this time, this method may
2360
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2361
- * containing failure details will be thrown.
2987
+ * Authenticates with Azure Active Directory and returns an access token if successful.
2988
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2362
2989
  *
2363
2990
  * If the token can't be retrieved silently, this method will require user interaction to retrieve the token.
2364
2991
  *
2992
+ * On Node.js, this credential has [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) enabled by default.
2993
+ * PKCE is a security feature that mitigates authentication code interception attacks.
2994
+ *
2365
2995
  * @param scopes - The list of scopes for which the token will have access.
2366
2996
  * @param options - The options used to configure any requests this
2367
2997
  * TokenCredential implementation might make.
2368
2998
  */
2369
- authenticate(scopes, options = {}) {
2370
- return tslib.__awaiter(this, void 0, void 0, function* () {
2371
- return trace(`${this.constructor.name}.authenticate`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
2372
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2373
- yield this.msalFlow.getToken(arrayScopes, newOptions);
2374
- return this.msalFlow.getActiveAccount();
2375
- }));
2999
+ async authenticate(scopes, options = {}) {
3000
+ return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => {
3001
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3002
+ await this.msalFlow.getToken(arrayScopes, newOptions);
3003
+ return this.msalFlow.getActiveAccount();
2376
3004
  });
2377
3005
  }
2378
3006
  }
@@ -2387,32 +3015,31 @@ class MsalDeviceCode extends MsalNode {
2387
3015
  super(options);
2388
3016
  this.userPromptCallback = options.userPromptCallback;
2389
3017
  }
2390
- doGetToken(scopes, options) {
2391
- return tslib.__awaiter(this, void 0, void 0, function* () {
2392
- try {
2393
- const requestOptions = {
2394
- deviceCodeCallback: this.userPromptCallback,
2395
- scopes,
2396
- cancel: false,
2397
- correlationId: options === null || options === void 0 ? void 0 : options.correlationId
2398
- };
2399
- const promise = this.publicApp.acquireTokenByDeviceCode(requestOptions);
2400
- // TODO:
2401
- // This should work, but it currently doesn't. I'm waiting for an answer from the MSAL team.
2402
- const deviceResponse = yield this.withCancellation(promise, options === null || options === void 0 ? void 0 : options.abortSignal, () => {
2403
- requestOptions.cancel = true;
2404
- });
2405
- return this.handleResult(scopes, this.clientId, deviceResponse || undefined);
2406
- }
2407
- catch (error) {
2408
- throw this.handleError(scopes, error, options);
2409
- }
2410
- });
3018
+ async doGetToken(scopes, options) {
3019
+ try {
3020
+ const requestOptions = {
3021
+ deviceCodeCallback: this.userPromptCallback,
3022
+ scopes,
3023
+ cancel: false,
3024
+ correlationId: options === null || options === void 0 ? void 0 : options.correlationId,
3025
+ authority: options === null || options === void 0 ? void 0 : options.authority
3026
+ };
3027
+ const promise = this.publicApp.acquireTokenByDeviceCode(requestOptions);
3028
+ // TODO:
3029
+ // This should work, but it currently doesn't. I'm waiting for an answer from the MSAL team.
3030
+ const deviceResponse = await this.withCancellation(promise, options === null || options === void 0 ? void 0 : options.abortSignal, () => {
3031
+ requestOptions.cancel = true;
3032
+ });
3033
+ return this.handleResult(scopes, this.clientId, deviceResponse || undefined);
3034
+ }
3035
+ catch (error) {
3036
+ throw this.handleError(scopes, error, options);
3037
+ }
2411
3038
  }
2412
3039
  }
2413
3040
 
2414
3041
  // Copyright (c) Microsoft Corporation.
2415
- const logger$e = credentialLogger("DeviceCodeCredential");
3042
+ const logger$g = credentialLogger("DeviceCodeCredential");
2416
3043
  /**
2417
3044
  * Method that logs the user code from the DeviceCodeCredential.
2418
3045
  * @param deviceCodeInfo - The device code.
@@ -2429,17 +3056,29 @@ class DeviceCodeCredential {
2429
3056
  * Creates an instance of DeviceCodeCredential with the details needed
2430
3057
  * to initiate the device code authorization flow with Azure Active Directory.
2431
3058
  *
3059
+ * A message will be logged, giving users a code that they can use to authenticate once they go to https://microsoft.com/devicelogin
3060
+ *
3061
+ * Developers can configure how this message is shown by passing a custom `userPromptCallback`:
3062
+ *
3063
+ * ```js
3064
+ * const credential = new DeviceCodeCredential({
3065
+ * tenantId: env.AZURE_TENANT_ID,
3066
+ * clientId: env.AZURE_CLIENT_ID,
3067
+ * userPromptCallback: (info) => {
3068
+ * console.log("CUSTOMIZED PROMPT CALLBACK", info.message);
3069
+ * }
3070
+ * });
3071
+ * ```
3072
+ *
2432
3073
  * @param options - Options for configuring the client which makes the authentication requests.
2433
3074
  */
2434
3075
  constructor(options) {
2435
- this.msalFlow = new MsalDeviceCode(Object.assign(Object.assign({}, options), { logger: logger$e, userPromptCallback: (options === null || options === void 0 ? void 0 : options.userPromptCallback) || defaultDeviceCodePromptCallback, tokenCredentialOptions: options || {} }));
3076
+ this.msalFlow = new MsalDeviceCode(Object.assign(Object.assign({}, options), { logger: logger$g, userPromptCallback: (options === null || options === void 0 ? void 0 : options.userPromptCallback) || defaultDeviceCodePromptCallback, tokenCredentialOptions: options || {} }));
2436
3077
  this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
2437
3078
  }
2438
3079
  /**
2439
- * Authenticates with Azure Active Directory and returns an access token if
2440
- * successful. If authentication cannot be performed at this time, this method may
2441
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2442
- * containing failure details will be thrown.
3080
+ * Authenticates with Azure Active Directory and returns an access token if successful.
3081
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2443
3082
  *
2444
3083
  * If the user provided the option `disableAutomaticAuthentication`,
2445
3084
  * once the token can't be retrieved silently,
@@ -2449,19 +3088,15 @@ class DeviceCodeCredential {
2449
3088
  * @param options - The options used to configure any requests this
2450
3089
  * TokenCredential implementation might make.
2451
3090
  */
2452
- getToken(scopes, options = {}) {
2453
- return tslib.__awaiter(this, void 0, void 0, function* () {
2454
- return trace(`${this.constructor.name}.getToken`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
2455
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2456
- return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
2457
- }));
3091
+ async getToken(scopes, options = {}) {
3092
+ return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3093
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3094
+ return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
2458
3095
  });
2459
3096
  }
2460
3097
  /**
2461
- * Authenticates with Azure Active Directory and returns an access token if
2462
- * successful. If authentication cannot be performed at this time, this method may
2463
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2464
- * containing failure details will be thrown.
3098
+ * Authenticates with Azure Active Directory and returns an access token if successful.
3099
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2465
3100
  *
2466
3101
  * If the token can't be retrieved silently, this method will require user interaction to retrieve the token.
2467
3102
  *
@@ -2469,19 +3104,55 @@ class DeviceCodeCredential {
2469
3104
  * @param options - The options used to configure any requests this
2470
3105
  * TokenCredential implementation might make.
2471
3106
  */
2472
- authenticate(scopes, options = {}) {
2473
- return tslib.__awaiter(this, void 0, void 0, function* () {
2474
- return trace(`${this.constructor.name}.authenticate`, options, (newOptions) => tslib.__awaiter(this, void 0, void 0, function* () {
2475
- const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
2476
- yield this.msalFlow.getToken(arrayScopes, newOptions);
2477
- return this.msalFlow.getActiveAccount();
2478
- }));
3107
+ async authenticate(scopes, options = {}) {
3108
+ return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => {
3109
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3110
+ await this.msalFlow.getToken(arrayScopes, newOptions);
3111
+ return this.msalFlow.getActiveAccount();
2479
3112
  });
2480
3113
  }
2481
3114
  }
2482
3115
 
2483
3116
  // Copyright (c) Microsoft Corporation.
2484
- const logger$f = credentialLogger("AuthorizationCodeCredential");
3117
+ /**
3118
+ * This MSAL client sets up a web server to listen for redirect callbacks, then calls to the MSAL's public application's `acquireTokenByDeviceCode` during `doGetToken`
3119
+ * to trigger the authentication flow, and then respond based on the values obtained from the redirect callback
3120
+ * @internal
3121
+ */
3122
+ class MsalAuthorizationCode extends MsalNode {
3123
+ constructor(options) {
3124
+ super(options);
3125
+ this.logger = credentialLogger("Node.js MSAL Authorization Code");
3126
+ this.redirectUri = options.redirectUri;
3127
+ this.authorizationCode = options.authorizationCode;
3128
+ if (options.clientSecret) {
3129
+ this.msalConfig.auth.clientSecret = options.clientSecret;
3130
+ }
3131
+ }
3132
+ async getAuthCodeUrl(options) {
3133
+ await this.init();
3134
+ return this.confidentialApp.getAuthCodeUrl(options);
3135
+ }
3136
+ async doGetToken(scopes, options) {
3137
+ var _a;
3138
+ try {
3139
+ const result = await ((_a = this.confidentialApp) === null || _a === void 0 ? void 0 : _a.acquireTokenByCode({
3140
+ scopes,
3141
+ redirectUri: this.redirectUri,
3142
+ code: this.authorizationCode
3143
+ }));
3144
+ // The Client Credential flow does not return an account,
3145
+ // so each time getToken gets called, we will have to acquire a new token through the service.
3146
+ return this.handleResult(scopes, this.clientId, result || undefined);
3147
+ }
3148
+ catch (err) {
3149
+ throw this.handleError(scopes, err, options);
3150
+ }
3151
+ }
3152
+ }
3153
+
3154
+ // Copyright (c) Microsoft Corporation.
3155
+ const logger$h = credentialLogger("AuthorizationCodeCredential");
2485
3156
  /**
2486
3157
  * Enables authentication to Azure Active Directory using an authorization code
2487
3158
  * that was obtained through the authorization code flow, described in more detail
@@ -2495,94 +3166,141 @@ class AuthorizationCodeCredential {
2495
3166
  * @internal
2496
3167
  */
2497
3168
  constructor(tenantId, clientId, clientSecretOrAuthorizationCode, authorizationCodeOrRedirectUri, redirectUriOrOptions, options) {
2498
- this.lastTokenResponse = null;
2499
- checkTenantId(logger$f, tenantId);
2500
- this.clientId = clientId;
2501
- this.tenantId = tenantId;
3169
+ checkTenantId(logger$h, tenantId);
3170
+ let clientSecret = clientSecretOrAuthorizationCode;
2502
3171
  if (typeof redirectUriOrOptions === "string") {
2503
3172
  // the clientId+clientSecret constructor
2504
- this.clientSecret = clientSecretOrAuthorizationCode;
2505
3173
  this.authorizationCode = authorizationCodeOrRedirectUri;
2506
3174
  this.redirectUri = redirectUriOrOptions;
2507
3175
  // options okay
2508
3176
  }
2509
3177
  else {
2510
3178
  // clientId only
2511
- this.clientSecret = undefined;
2512
3179
  this.authorizationCode = clientSecretOrAuthorizationCode;
2513
3180
  this.redirectUri = authorizationCodeOrRedirectUri;
3181
+ clientSecret = undefined;
2514
3182
  options = redirectUriOrOptions;
2515
3183
  }
2516
- this.identityClient = new IdentityClient(options);
3184
+ this.msalFlow = new MsalAuthorizationCode(Object.assign(Object.assign({}, options), { clientSecret,
3185
+ clientId, tokenCredentialOptions: options || {}, logger: logger$h, redirectUri: this.redirectUri, authorizationCode: this.authorizationCode }));
2517
3186
  }
2518
3187
  /**
2519
- * Authenticates with Azure Active Directory and returns an access token if
2520
- * successful. If authentication cannot be performed at this time, this method may
2521
- * return null. If an error occurs during authentication, an {@link AuthenticationError}
2522
- * containing failure details will be thrown.
3188
+ * Authenticates with Azure Active Directory and returns an access token if successful.
3189
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
2523
3190
  *
2524
3191
  * @param scopes - The list of scopes for which the token will have access.
2525
3192
  * @param options - The options used to configure any requests this
2526
3193
  * TokenCredential implementation might make.
2527
3194
  */
2528
- getToken(scopes, options) {
2529
- var _a, _b;
2530
- return tslib.__awaiter(this, void 0, void 0, function* () {
2531
- const { span, updatedOptions } = createSpan("AuthorizationCodeCredential-getToken", options);
3195
+ async getToken(scopes, options = {}) {
3196
+ return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
3197
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3198
+ return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
3199
+ });
3200
+ }
3201
+ }
3202
+
3203
+ // Copyright (c) Microsoft Corporation.
3204
+ /**
3205
+ * MSAL on behalf of flow. Calls to MSAL's confidential application's `acquireTokenOnBehalfOf` during `doGetToken`.
3206
+ * @internal
3207
+ */
3208
+ class MsalOnBehalfOf extends MsalNode {
3209
+ constructor(options) {
3210
+ super(options);
3211
+ this.logger.info("Initialized MSAL's On-Behalf-Of flow");
3212
+ this.requiresConfidential = true;
3213
+ this.userAssertionToken = options.userAssertionToken;
3214
+ this.certificatePath = options.certificatePath;
3215
+ this.sendCertificateChain = options.sendCertificateChain;
3216
+ this.clientSecret = options.clientSecret;
3217
+ }
3218
+ // Changing the MSAL configuration asynchronously
3219
+ async init(options) {
3220
+ if (this.certificatePath) {
2532
3221
  try {
2533
- let tokenResponse = null;
2534
- let scopeString = typeof scopes === "string" ? scopes : scopes.join(" ");
2535
- if (scopeString.indexOf("offline_access") < 0) {
2536
- scopeString += " offline_access";
2537
- }
2538
- // Try to use the refresh token first
2539
- if (this.lastTokenResponse && this.lastTokenResponse.refreshToken) {
2540
- tokenResponse = yield this.identityClient.refreshAccessToken(this.tenantId, this.clientId, scopeString, this.lastTokenResponse.refreshToken, this.clientSecret, undefined, updatedOptions);
2541
- }
2542
- if (tokenResponse === null) {
2543
- const urlSuffix = getIdentityTokenEndpointSuffix(this.tenantId);
2544
- const webResource = this.identityClient.createWebResource({
2545
- url: `${this.identityClient.authorityHost}/${this.tenantId}/${urlSuffix}`,
2546
- method: "POST",
2547
- disableJsonStringifyOnBody: true,
2548
- deserializationMapper: undefined,
2549
- body: qs.stringify({
2550
- client_id: this.clientId,
2551
- grant_type: "authorization_code",
2552
- scope: scopeString,
2553
- code: this.authorizationCode,
2554
- redirect_uri: this.redirectUri,
2555
- client_secret: this.clientSecret
2556
- }),
2557
- headers: {
2558
- Accept: "application/json",
2559
- "Content-Type": "application/x-www-form-urlencoded"
2560
- },
2561
- abortSignal: options && options.abortSignal,
2562
- spanOptions: (_a = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _a === void 0 ? void 0 : _a.spanOptions,
2563
- tracingContext: (_b = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _b === void 0 ? void 0 : _b.tracingContext
2564
- });
2565
- tokenResponse = yield this.identityClient.sendTokenRequest(webResource);
2566
- }
2567
- this.lastTokenResponse = tokenResponse;
2568
- logger$f.getToken.info(formatSuccess(scopes));
2569
- const token = tokenResponse && tokenResponse.accessToken;
2570
- if (!token) {
2571
- throw new CredentialUnavailableError("Failed to retrieve a valid token");
2572
- }
2573
- return token;
2574
- }
2575
- catch (err) {
2576
- span.setStatus({
2577
- code: coreTracing.SpanStatusCode.ERROR,
2578
- message: err.message
2579
- });
2580
- logger$f.getToken.info(formatError(scopes, err));
2581
- throw err;
3222
+ const parts = await parseCertificate({ certificatePath: this.certificatePath }, this.sendCertificateChain);
3223
+ this.msalConfig.auth.clientCertificate = {
3224
+ thumbprint: parts.thumbprint,
3225
+ privateKey: parts.certificateContents,
3226
+ x5c: parts.x5c
3227
+ };
2582
3228
  }
2583
- finally {
2584
- span.end();
3229
+ catch (error) {
3230
+ this.logger.info(formatError("", error));
3231
+ throw error;
2585
3232
  }
3233
+ }
3234
+ else {
3235
+ this.msalConfig.auth.clientSecret = this.clientSecret;
3236
+ }
3237
+ return super.init(options);
3238
+ }
3239
+ async doGetToken(scopes, options = {}) {
3240
+ try {
3241
+ const result = await this.confidentialApp.acquireTokenOnBehalfOf({
3242
+ scopes,
3243
+ correlationId: options.correlationId,
3244
+ authority: options.authority,
3245
+ oboAssertion: this.userAssertionToken
3246
+ });
3247
+ return this.handleResult(scopes, this.clientId, result || undefined);
3248
+ }
3249
+ catch (err) {
3250
+ throw this.handleError(scopes, err, options);
3251
+ }
3252
+ }
3253
+ }
3254
+
3255
+ // Copyright (c) Microsoft Corporation.
3256
+ const credentialName$1 = "OnBehalfOfCredential";
3257
+ const logger$i = credentialLogger(credentialName$1);
3258
+ /**
3259
+ * Enables authentication to Azure Active Directory using the [On Behalf Of flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow).
3260
+ */
3261
+ class OnBehalfOfCredential {
3262
+ /**
3263
+ * Creates an instance of the {@link OnBehalfOfCredential} with the details
3264
+ * needed to authenticate against Azure Active Directory with a client
3265
+ * secret or a path to a PEM certificate, and an user assertion.
3266
+ *
3267
+ * Example using the `KeyClient` from [\@azure/keyvault-keys](https://www.npmjs.com/package/\@azure/keyvault-keys):
3268
+ *
3269
+ * ```ts
3270
+ * const tokenCredential = new OnBehalfOfCredential({
3271
+ * tenantId,
3272
+ * clientId,
3273
+ * clientSecret, // or `certificatePath: "/path/to/certificate.pem"
3274
+ * userAssertionToken: "access-token"
3275
+ * });
3276
+ * const client = new KeyClient("vault-url", tokenCredential);
3277
+ *
3278
+ * await client.getKey("key-name");
3279
+ * ```
3280
+ *
3281
+ * @param options - Optional parameters, generally common across credentials.
3282
+ */
3283
+ constructor(options) {
3284
+ this.options = options;
3285
+ const { clientSecret } = options;
3286
+ const { certificatePath } = options;
3287
+ const { tenantId, clientId, userAssertionToken } = options;
3288
+ if (!tenantId || !clientId || !(clientSecret || certificatePath) || !userAssertionToken) {
3289
+ throw new Error(`${credentialName$1}: tenantId, clientId, clientSecret (or certificatePath) and userAssertionToken are required parameters.`);
3290
+ }
3291
+ this.msalFlow = new MsalOnBehalfOf(Object.assign(Object.assign({}, this.options), { logger: logger$i, tokenCredentialOptions: this.options }));
3292
+ }
3293
+ /**
3294
+ * Authenticates with Azure Active Directory and returns an access token if successful.
3295
+ * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
3296
+ *
3297
+ * @param scopes - The list of scopes for which the token will have access.
3298
+ * @param options - The options used to configure the underlying network requests.
3299
+ */
3300
+ async getToken(scopes, options = {}) {
3301
+ return trace(`${credentialName$1}.getToken`, options, async (newOptions) => {
3302
+ const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
3303
+ return this.msalFlow.getToken(arrayScopes, newOptions);
2586
3304
  });
2587
3305
  }
2588
3306
  }
@@ -2613,9 +3331,12 @@ exports.DeviceCodeCredential = DeviceCodeCredential;
2613
3331
  exports.EnvironmentCredential = EnvironmentCredential;
2614
3332
  exports.InteractiveBrowserCredential = InteractiveBrowserCredential;
2615
3333
  exports.ManagedIdentityCredential = ManagedIdentityCredential;
3334
+ exports.OnBehalfOfCredential = OnBehalfOfCredential;
2616
3335
  exports.UsernamePasswordCredential = UsernamePasswordCredential;
3336
+ exports.VisualStudioCodeCredential = VisualStudioCodeCredential;
2617
3337
  exports.deserializeAuthenticationRecord = deserializeAuthenticationRecord;
2618
3338
  exports.getDefaultAzureCredential = getDefaultAzureCredential;
2619
3339
  exports.logger = logger;
2620
3340
  exports.serializeAuthenticationRecord = serializeAuthenticationRecord;
3341
+ exports.useIdentityPlugin = useIdentityPlugin;
2621
3342
  //# sourceMappingURL=index.js.map