@azure/identity 2.0.0-beta.2 → 2.0.0-beta.6

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