@azure/identity 4.5.0-beta.1 → 4.5.0-beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/README.md +22 -1
  2. package/dist/index.js +238 -112
  3. package/dist/index.js.map +1 -1
  4. package/dist-esm/src/client/identityClient.js +1 -2
  5. package/dist-esm/src/client/identityClient.js.map +1 -1
  6. package/dist-esm/src/constants.js +1 -1
  7. package/dist-esm/src/constants.js.map +1 -1
  8. package/dist-esm/src/credentials/azurePipelinesCredential.js +50 -26
  9. package/dist-esm/src/credentials/azurePipelinesCredential.js.map +1 -1
  10. package/dist-esm/src/credentials/azurePowerShellCredential.js +63 -19
  11. package/dist-esm/src/credentials/azurePowerShellCredential.js.map +1 -1
  12. package/dist-esm/src/credentials/clientAssertionCredential.js +9 -2
  13. package/dist-esm/src/credentials/clientAssertionCredential.js.map +1 -1
  14. package/dist-esm/src/credentials/clientCertificateCredential.js +35 -27
  15. package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
  16. package/dist-esm/src/credentials/clientSecretCredential.js +9 -2
  17. package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
  18. package/dist-esm/src/credentials/deviceCodeCredential.js +1 -1
  19. package/dist-esm/src/credentials/deviceCodeCredential.js.map +1 -1
  20. package/dist-esm/src/credentials/deviceCodeCredentialOptions.js.map +1 -1
  21. package/dist-esm/src/credentials/environmentCredential.js +11 -1
  22. package/dist-esm/src/credentials/environmentCredential.js.map +1 -1
  23. package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js.map +1 -1
  24. package/dist-esm/src/credentials/interactiveBrowserCredential.js +1 -1
  25. package/dist-esm/src/credentials/interactiveBrowserCredential.js.map +1 -1
  26. package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js.map +1 -1
  27. package/dist-esm/src/credentials/managedIdentityCredential/msalMsiProvider.js +6 -5
  28. package/dist-esm/src/credentials/managedIdentityCredential/msalMsiProvider.js.map +1 -1
  29. package/dist-esm/src/credentials/onBehalfOfCredential.js +16 -9
  30. package/dist-esm/src/credentials/onBehalfOfCredential.js.map +1 -1
  31. package/dist-esm/src/credentials/usernamePasswordCredential.js +13 -3
  32. package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -1
  33. package/dist-esm/src/credentials/workloadIdentityCredential.js +16 -6
  34. package/dist-esm/src/credentials/workloadIdentityCredential.js.map +1 -1
  35. package/dist-esm/src/errors.js +12 -7
  36. package/dist-esm/src/errors.js.map +1 -1
  37. package/dist-esm/src/msal/browserFlows/flows.js.map +1 -0
  38. package/dist-esm/src/msal/browserFlows/msalBrowserCommon.js.map +1 -1
  39. package/dist-esm/src/msal/nodeFlows/msalClient.js +8 -1
  40. package/dist-esm/src/msal/nodeFlows/msalClient.js.map +1 -1
  41. package/package.json +3 -3
  42. package/types/identity.d.ts +26 -8
  43. package/dist-esm/src/msal/flows.js.map +0 -1
  44. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js +0 -47
  45. package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js.map +0 -1
  46. package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js +0 -42
  47. package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js.map +0 -1
  48. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js +0 -112
  49. package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js.map +0 -1
  50. package/dist-esm/src/msal/nodeFlows/msalClientSecret.js +0 -33
  51. package/dist-esm/src/msal/nodeFlows/msalClientSecret.js.map +0 -1
  52. package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js +0 -35
  53. package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js.map +0 -1
  54. package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js +0 -323
  55. package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js.map +0 -1
  56. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js +0 -58
  57. package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js.map +0 -1
  58. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js +0 -113
  59. package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js.map +0 -1
  60. package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js +0 -33
  61. package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js.map +0 -1
  62. /package/dist-esm/src/msal/{flows.js → browserFlows/flows.js} +0 -0
package/README.md CHANGED
@@ -213,7 +213,7 @@ For examples of how to use managed identity for authentication, see [the example
213
213
 
214
214
  ## Cloud configuration
215
215
 
216
- Credentials default to authenticating to the Microsoft Entra endpoint for Azure Public Cloud. To access resources in other clouds, such as Azure Government or a private cloud, configure credentials with the `authorityHost` argument in the constructor. The `AzureAuthorityHosts` interface defines authorities for well-known clouds. For the US Government cloud, you could instantiate a credential this way:
216
+ Credentials default to authenticating to the Microsoft Entra endpoint for Azure Public Cloud. To access resources in other clouds, such as Azure Government or a private cloud, configure credentials with the `authorityHost` argument in the constructor. The [`AzureAuthorityHosts`][authority_hosts] enum defines authorities for well-known clouds. For the US Government cloud, you could instantiate a credential this way:
217
217
 
218
218
  ```typescript
219
219
  import { AzureAuthorityHosts, ClientSecretCredential } from "@azure/identity";
@@ -227,6 +227,26 @@ const credential = new ClientSecretCredential(
227
227
  );
228
228
  ```
229
229
 
230
+ As an alternative to specifying the `authorityHost` argument, you can also set the `AZURE_AUTHORITY_HOST` environment variable to the URL of your cloud's authority. This approach is useful when configuring multiple credentials to authenticate to the same cloud or when the deployed environment needs to define the target cloud:
231
+
232
+ ```sh
233
+ AZURE_AUTHORITY_HOST=https://login.partner.microsoftonline.cn
234
+ ```
235
+
236
+ The `AzureAuthorityHosts` enum defines authorities for well-known clouds for your convenience; however, if the authority for your cloud isn't listed in `AzureAuthorityHosts`, you may pass any valid authority URL as a string argument. For example:
237
+
238
+ ```typescript
239
+ import { AzureAuthorityHosts, ClientSecretCredential } from "@azure/identity";
240
+ const credential = new ClientSecretCredential(
241
+ "<YOUR_TENANT_ID>",
242
+ "<YOUR_CLIENT_ID>",
243
+ "<YOUR_CLIENT_SECRET>",
244
+ {
245
+ authorityHost: "https://login.partner.microsoftonline.cn",
246
+ }
247
+ );
248
+ ```
249
+
230
250
  Not all credentials require this configuration. Credentials that authenticate through a development tool, such as `AzureCliCredential`, use that tool's configuration. Similarly, `VisualStudioCodeCredential` accepts an `authorityHost` argument but defaults to the `authorityHost` matching Visual Studio Code's **Azure: Cloud** setting.
231
251
 
232
252
  ## Credential classes
@@ -369,5 +389,6 @@ If you'd like to contribute to this library, please read the [contributing guide
369
389
  [defaultauthflow_image]: https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/identity/identity/images/mermaidjs/DefaultAzureCredentialAuthFlow.svg
370
390
  [azure_identity_broker]: https://www.npmjs.com/package/@azure/identity-broker
371
391
  [azure_identity_broker_readme]: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/identity/identity-broker
392
+ [authority_hosts]: https://learn.microsoft.com/javascript/api/@azure/identity/azureauthorityhosts
372
393
 
373
394
  ![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-js%2Fsdk%2Fidentity%2Fidentity%2FREADME.png)
package/dist/index.js CHANGED
@@ -6,18 +6,18 @@ var logger$m = require('@azure/logger');
6
6
  var coreClient = require('@azure/core-client');
7
7
  var coreUtil = require('@azure/core-util');
8
8
  var coreRestPipeline = require('@azure/core-rest-pipeline');
9
- var abortController = require('@azure/abort-controller');
10
9
  var coreTracing = require('@azure/core-tracing');
11
10
  var fs = require('fs');
12
11
  var os = require('os');
13
12
  var path = require('path');
13
+ var abortController = require('@azure/abort-controller');
14
14
  var msalCommon = require('@azure/msal-node');
15
15
  var open = require('open');
16
16
  var promises = require('fs/promises');
17
17
  var child_process = require('child_process');
18
18
  var crypto = require('crypto');
19
- var promises$1 = require('node:fs/promises');
20
19
  var node_crypto = require('node:crypto');
20
+ var promises$1 = require('node:fs/promises');
21
21
 
22
22
  function _interopNamespaceDefault(e) {
23
23
  var n = Object.create(null);
@@ -44,7 +44,7 @@ var child_process__namespace = /*#__PURE__*/_interopNamespaceDefault(child_proce
44
44
  /**
45
45
  * Current version of the `@azure/identity` package.
46
46
  */
47
- const SDK_VERSION = `4.5.0-beta.1`;
47
+ const SDK_VERSION = `4.5.0-beta.2`;
48
48
  /**
49
49
  * The default client ID for authentication
50
50
  * @internal
@@ -289,8 +289,9 @@ const CredentialUnavailableErrorName = "CredentialUnavailableError";
289
289
  * an error that should halt the chain, it's caught and the chain continues
290
290
  */
291
291
  class CredentialUnavailableError extends Error {
292
- constructor(message) {
293
- super(message);
292
+ constructor(message, options) {
293
+ // @ts-expect-error - TypeScript does not recognize this until we use ES2022 as the target; however, all our major runtimes do support the `cause` property
294
+ super(message, options);
294
295
  this.name = CredentialUnavailableErrorName;
295
296
  }
296
297
  }
@@ -305,7 +306,7 @@ const AuthenticationErrorName = "AuthenticationError";
305
306
  */
306
307
  class AuthenticationError extends Error {
307
308
  // eslint-disable-next-line @typescript-eslint/ban-types
308
- constructor(statusCode, errorBody) {
309
+ constructor(statusCode, errorBody, options) {
309
310
  let errorResponse = {
310
311
  error: "unknown",
311
312
  errorDescription: "An unknown error occurred and no additional details are available.",
@@ -323,8 +324,8 @@ class AuthenticationError extends Error {
323
324
  catch (e) {
324
325
  if (statusCode === 400) {
325
326
  errorResponse = {
326
- error: "authority_not_found",
327
- errorDescription: "The specified authority URL was not found.",
327
+ error: "invalid_request",
328
+ errorDescription: `The service indicated that the request was invalid.\n\n${errorBody}`,
328
329
  };
329
330
  }
330
331
  else {
@@ -341,7 +342,9 @@ class AuthenticationError extends Error {
341
342
  errorDescription: "An unknown error occurred and no additional details are available.",
342
343
  };
343
344
  }
344
- super(`${errorResponse.error} Status code: ${statusCode}\nMore details:\n${errorResponse.errorDescription}`);
345
+ super(`${errorResponse.error} Status code: ${statusCode}\nMore details:\n${errorResponse.errorDescription},`,
346
+ // @ts-expect-error - TypeScript does not recognize this until we use ES2022 as the target; however, all our major runtimes do support the `cause` property
347
+ options);
345
348
  this.statusCode = statusCode;
346
349
  this.errorResponse = errorResponse;
347
350
  // Ensure that this type reports the correct name
@@ -384,7 +387,9 @@ class AuthenticationRequiredError extends Error {
384
387
  * Optional parameters. A message can be specified. The {@link GetTokenOptions} of the request can also be specified to more easily associate the error with the received parameters.
385
388
  */
386
389
  options) {
387
- super(options.message);
390
+ super(options.message,
391
+ // @ts-expect-error - TypeScript does not recognize this until we use ES2022 as the target; however, all our major runtimes do support the `cause` property
392
+ options.cause ? { cause: options.cause } : undefined);
388
393
  this.scopes = options.scopes;
389
394
  this.getTokenOptions = options.getTokenOptions;
390
395
  this.name = "AuthenticationRequiredError";
@@ -674,7 +679,7 @@ class IdentityClient extends coreClient.ServiceClient {
674
679
  // Here is a custom layer that allows us to abort requests that go through MSAL,
675
680
  // since MSAL doesn't allow us to pass options all the way through.
676
681
  generateAbortSignal(correlationId) {
677
- const controller = new abortController.AbortController();
682
+ const controller = new AbortController();
678
683
  const controllers = this.abortControllers.get(correlationId) || [];
679
684
  controllers.push(controller);
680
685
  this.abortControllers.set(correlationId, controllers);
@@ -682,7 +687,7 @@ class IdentityClient extends coreClient.ServiceClient {
682
687
  controller.signal.onabort = (...params) => {
683
688
  this.abortControllers.set(correlationId, undefined);
684
689
  if (existingOnAbort) {
685
- existingOnAbort(...params);
690
+ existingOnAbort.apply(controller.signal, params);
686
691
  }
687
692
  };
688
693
  return controller.signal;
@@ -1524,6 +1529,10 @@ function calculateRegionalAuthority(regionalAuthority) {
1524
1529
 
1525
1530
  // Copyright (c) Microsoft Corporation.
1526
1531
  // Licensed under the MIT license.
1532
+ /**
1533
+ * The default logger used if no logger was passed in by the credential.
1534
+ */
1535
+ const msalLogger = credentialLogger("MsalClient");
1527
1536
  /**
1528
1537
  * A call to open(), but mockable
1529
1538
  * @internal
@@ -1531,13 +1540,6 @@ function calculateRegionalAuthority(regionalAuthority) {
1531
1540
  const interactiveBrowserMockable = {
1532
1541
  open,
1533
1542
  };
1534
-
1535
- // Copyright (c) Microsoft Corporation.
1536
- // Licensed under the MIT license.
1537
- /**
1538
- * The default logger used if no logger was passed in by the credential.
1539
- */
1540
- const msalLogger = credentialLogger("MsalClient");
1541
1543
  /**
1542
1544
  * Generates the configuration for MSAL (Microsoft Authentication Library).
1543
1545
  *
@@ -1983,8 +1985,14 @@ class ClientAssertionCredential {
1983
1985
  * @param options - Options for configuring the client which makes the authentication request.
1984
1986
  */
1985
1987
  constructor(tenantId, clientId, getAssertion, options = {}) {
1986
- if (!tenantId || !clientId || !getAssertion) {
1987
- throw new Error("ClientAssertionCredential: tenantId, clientId, and clientAssertion are required parameters.");
1988
+ if (!tenantId) {
1989
+ throw new CredentialUnavailableError("ClientAssertionCredential: tenantId is a required parameter.");
1990
+ }
1991
+ if (!clientId) {
1992
+ throw new CredentialUnavailableError("ClientAssertionCredential: clientId is a required parameter.");
1993
+ }
1994
+ if (!getAssertion) {
1995
+ throw new CredentialUnavailableError("ClientAssertionCredential: clientAssertion is a required parameter.");
1988
1996
  }
1989
1997
  this.tenantId = tenantId;
1990
1998
  this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
@@ -2059,10 +2067,20 @@ class WorkloadIdentityCredential {
2059
2067
  if (tenantId) {
2060
2068
  checkTenantId(logger$g, tenantId);
2061
2069
  }
2062
- if (clientId && tenantId && this.federatedTokenFilePath) {
2063
- logger$g.info(`Invoking ClientAssertionCredential with tenant ID: ${tenantId}, clientId: ${workloadIdentityCredentialOptions.clientId} and federated token path: [REDACTED]`);
2064
- this.client = new ClientAssertionCredential(tenantId, clientId, this.readFileContents.bind(this), options);
2070
+ if (!clientId) {
2071
+ throw new CredentialUnavailableError(`${credentialName$4}: is unavailable. clientId is a required parameter. In DefaultAzureCredential and ManagedIdentityCredential, this can be provided as an environment variable - "AZURE_CLIENT_ID".
2072
+ See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot`);
2065
2073
  }
2074
+ if (!tenantId) {
2075
+ throw new CredentialUnavailableError(`${credentialName$4}: is unavailable. tenantId is a required parameter. In DefaultAzureCredential and ManagedIdentityCredential, this can be provided as an environment variable - "AZURE_TENANT_ID".
2076
+ See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot`);
2077
+ }
2078
+ if (!this.federatedTokenFilePath) {
2079
+ throw new CredentialUnavailableError(`${credentialName$4}: is unavailable. federatedTokenFilePath is a required parameter. In DefaultAzureCredential and ManagedIdentityCredential, this can be provided as an environment variable - "AZURE_FEDERATED_TOKEN_FILE".
2080
+ See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot`);
2081
+ }
2082
+ logger$g.info(`Invoking ClientAssertionCredential with tenant ID: ${tenantId}, clientId: ${workloadIdentityCredentialOptions.clientId} and federated token path: [REDACTED]`);
2083
+ this.client = new ClientAssertionCredential(tenantId, clientId, this.readFileContents.bind(this), options);
2066
2084
  }
2067
2085
  /**
2068
2086
  * Authenticates with Microsoft Entra ID and returns an access token if successful.
@@ -2078,7 +2096,7 @@ class WorkloadIdentityCredential {
2078
2096
  In DefaultAzureCredential and ManagedIdentityCredential, these can be provided as environment variables -
2079
2097
  "AZURE_TENANT_ID",
2080
2098
  "AZURE_CLIENT_ID",
2081
- "AZURE_FEDERATED_TOKEN_FILE". See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot `;
2099
+ "AZURE_FEDERATED_TOKEN_FILE". See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot`;
2082
2100
  logger$g.info(errorMessage);
2083
2101
  throw new CredentialUnavailableError(errorMessage);
2084
2102
  }
@@ -2217,6 +2235,7 @@ class MsalMsiProvider {
2217
2235
  // We will continue to implement these features in the Identity library.
2218
2236
  const identitySource = this.managedIdentityApp.getManagedIdentitySource();
2219
2237
  const isImdsMsi = identitySource === "DefaultToImds" || identitySource === "Imds"; // Neither actually checks that IMDS endpoint is available, just that it's the source the MSAL _would_ try to use.
2238
+ logger$e.getToken.info(`MSAL Identity source: ${identitySource}`);
2220
2239
  if (isTokenExchangeMsi) {
2221
2240
  // In the AKS scenario we will use the existing tokenExchangeMsi indefinitely.
2222
2241
  logger$e.getToken.info("Using the token exchange managed identity.");
@@ -2228,7 +2247,7 @@ class MsalMsiProvider {
2228
2247
  resourceId: this.resourceId,
2229
2248
  });
2230
2249
  if (result === null) {
2231
- throw new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
2250
+ throw new CredentialUnavailableError("Attempted to use the token exchange managed identity, but received a null response.");
2232
2251
  }
2233
2252
  return result;
2234
2253
  }
@@ -2244,7 +2263,7 @@ class MsalMsiProvider {
2244
2263
  resourceId: this.resourceId,
2245
2264
  });
2246
2265
  if (!isAvailable) {
2247
- throw new CredentialUnavailableError(`ManagedIdentityCredential: The managed identity endpoint is not available.`);
2266
+ throw new CredentialUnavailableError(`ManagedIdentityCredential: Attempted to use the IMDS endpoint, but it is not available.`);
2248
2267
  }
2249
2268
  }
2250
2269
  // If we got this far, it means:
@@ -2270,9 +2289,9 @@ class MsalMsiProvider {
2270
2289
  throw err;
2271
2290
  }
2272
2291
  if (isNetworkError(err)) {
2273
- throw new CredentialUnavailableError(`ManagedIdentityCredential: Network unreachable. Message: ${err.message}`);
2292
+ throw new CredentialUnavailableError(`ManagedIdentityCredential: Network unreachable. Message: ${err.message}`, { cause: err });
2274
2293
  }
2275
- throw new CredentialUnavailableError(`ManagedIdentityCredential: Authentication failed. Message ${err.message}`);
2294
+ throw new CredentialUnavailableError(`ManagedIdentityCredential: Authentication failed. Message ${err.message}`, { cause: err });
2276
2295
  }
2277
2296
  });
2278
2297
  }
@@ -2289,7 +2308,7 @@ class MsalMsiProvider {
2289
2308
  });
2290
2309
  };
2291
2310
  if (!msalToken) {
2292
- throw createError("No response");
2311
+ throw createError("No response.");
2293
2312
  }
2294
2313
  if (!msalToken.expiresOn) {
2295
2314
  throw createError(`Response had no "expiresOn" property.`);
@@ -2848,33 +2867,45 @@ class AzurePowerShellCredential {
2848
2867
  commandStack.shift();
2849
2868
  continue;
2850
2869
  }
2851
- let tenantSection = "";
2852
- if (tenantId) {
2853
- tenantSection = `-TenantId "${tenantId}"`;
2854
- }
2855
2870
  const results = await runCommands([
2856
2871
  [
2857
2872
  powerShellCommand,
2858
2873
  "-NoProfile",
2859
2874
  "-NonInteractive",
2860
2875
  "-Command",
2861
- "Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru",
2862
- ],
2863
- [
2864
- powerShellCommand,
2865
- "-NoProfile",
2866
- "-NonInteractive",
2867
- "-Command",
2868
- `Get-AzAccessToken ${tenantSection} -ResourceUrl "${resource}" | ConvertTo-Json`,
2876
+ `
2877
+ $tenantId = "${tenantId !== null && tenantId !== void 0 ? tenantId : ""}"
2878
+ $m = Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru
2879
+ $useSecureString = $m.Version -ge [version]'2.17.0'
2880
+
2881
+ $params = @{
2882
+ ResourceUrl = "${resource}"
2883
+ }
2884
+
2885
+ if ($tenantId.Length -gt 0) {
2886
+ $params["TenantId"] = $tenantId
2887
+ }
2888
+
2889
+ if ($useSecureString) {
2890
+ $params["AsSecureString"] = $true
2891
+ }
2892
+
2893
+ $token = Get-AzAccessToken @params
2894
+
2895
+ $result = New-Object -TypeName PSObject
2896
+ $result | Add-Member -MemberType NoteProperty -Name ExpiresOn -Value $token.ExpiresOn
2897
+ if ($useSecureString) {
2898
+ $result | Add-Member -MemberType NoteProperty -Name Token -Value (ConvertFrom-SecureString -AsPlainText $token.Token)
2899
+ } else {
2900
+ $result | Add-Member -MemberType NoteProperty -Name Token -Value $token.Token
2901
+ }
2902
+
2903
+ Write-Output (ConvertTo-Json $result)
2904
+ `,
2869
2905
  ],
2870
2906
  ]);
2871
- const result = results[1];
2872
- try {
2873
- return JSON.parse(result);
2874
- }
2875
- catch (e) {
2876
- throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
2877
- }
2907
+ const result = results[0];
2908
+ return parseJsonToken(result);
2878
2909
  }
2879
2910
  throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`);
2880
2911
  }
@@ -2921,6 +2952,38 @@ class AzurePowerShellCredential {
2921
2952
  });
2922
2953
  }
2923
2954
  }
2955
+ /**
2956
+ *
2957
+ * @internal
2958
+ */
2959
+ async function parseJsonToken(result) {
2960
+ const jsonRegex = /{[^{}]*}/g;
2961
+ const matches = result.match(jsonRegex);
2962
+ let resultWithoutToken = result;
2963
+ if (matches) {
2964
+ try {
2965
+ for (const item of matches) {
2966
+ try {
2967
+ const jsonContent = JSON.parse(item);
2968
+ if (jsonContent === null || jsonContent === void 0 ? void 0 : jsonContent.Token) {
2969
+ resultWithoutToken = resultWithoutToken.replace(item, "");
2970
+ if (resultWithoutToken) {
2971
+ logger$b.getToken.warning(resultWithoutToken);
2972
+ }
2973
+ return jsonContent;
2974
+ }
2975
+ }
2976
+ catch (e) {
2977
+ continue;
2978
+ }
2979
+ }
2980
+ }
2981
+ catch (e) {
2982
+ throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
2983
+ }
2984
+ }
2985
+ throw new Error(`No access token found in the output. Received output: ${result}`);
2986
+ }
2924
2987
 
2925
2988
  // Copyright (c) Microsoft Corporation.
2926
2989
  // Licensed under the MIT license.
@@ -3053,7 +3116,8 @@ class ClientCertificateCredential {
3053
3116
  });
3054
3117
  }
3055
3118
  async buildClientCertificate() {
3056
- const parts = await this.parseCertificate();
3119
+ var _a;
3120
+ const parts = await parseCertificate(this.certificateConfiguration, (_a = this.sendCertificateChain) !== null && _a !== void 0 ? _a : false);
3057
3121
  let privateKey;
3058
3122
  if (this.certificateConfiguration.certificatePassword !== undefined) {
3059
3123
  privateKey = crypto.createPrivateKey({
@@ -3076,34 +3140,41 @@ class ClientCertificateCredential {
3076
3140
  x5c: parts.x5c,
3077
3141
  };
3078
3142
  }
3079
- async parseCertificate() {
3080
- const certificate = this.certificateConfiguration.certificate;
3081
- const certificatePath = this.certificateConfiguration.certificatePath;
3082
- const certificateContents = certificate || (await promises.readFile(certificatePath, "utf8"));
3083
- const x5c = this.sendCertificateChain ? certificateContents : undefined;
3084
- const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
3085
- const publicKeys = [];
3086
- // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
3087
- let match;
3088
- do {
3089
- match = certificatePattern.exec(certificateContents);
3090
- if (match) {
3091
- publicKeys.push(match[3]);
3092
- }
3093
- } while (match);
3094
- if (publicKeys.length === 0) {
3095
- throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
3143
+ }
3144
+ /**
3145
+ * Parses a certificate into its relevant parts
3146
+ *
3147
+ * @param certificateConfiguration - The certificate contents or path to the certificate
3148
+ * @param sendCertificateChain - true if the entire certificate chain should be sent for SNI, false otherwise
3149
+ * @returns The parsed certificate parts and the certificate contents
3150
+ */
3151
+ async function parseCertificate(certificateConfiguration, sendCertificateChain) {
3152
+ const certificate = certificateConfiguration.certificate;
3153
+ const certificatePath = certificateConfiguration.certificatePath;
3154
+ const certificateContents = certificate || (await promises.readFile(certificatePath, "utf8"));
3155
+ const x5c = sendCertificateChain ? certificateContents : undefined;
3156
+ const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
3157
+ const publicKeys = [];
3158
+ // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
3159
+ let match;
3160
+ do {
3161
+ match = certificatePattern.exec(certificateContents);
3162
+ if (match) {
3163
+ publicKeys.push(match[3]);
3096
3164
  }
3097
- const thumbprint = crypto.createHash("sha1")
3098
- .update(Buffer.from(publicKeys[0], "base64"))
3099
- .digest("hex")
3100
- .toUpperCase();
3101
- return {
3102
- certificateContents,
3103
- thumbprint,
3104
- x5c,
3105
- };
3106
- }
3165
+ } while (match);
3166
+ if (publicKeys.length === 0) {
3167
+ throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
3168
+ }
3169
+ const thumbprint = crypto.createHash("sha1")
3170
+ .update(Buffer.from(publicKeys[0], "base64"))
3171
+ .digest("hex")
3172
+ .toUpperCase();
3173
+ return {
3174
+ certificateContents,
3175
+ thumbprint,
3176
+ x5c,
3177
+ };
3107
3178
  }
3108
3179
 
3109
3180
  // Copyright (c) Microsoft Corporation.
@@ -3129,8 +3200,14 @@ class ClientSecretCredential {
3129
3200
  * @param options - Options for configuring the client which makes the authentication request.
3130
3201
  */
3131
3202
  constructor(tenantId, clientId, clientSecret, options = {}) {
3132
- if (!tenantId || !clientId || !clientSecret) {
3133
- throw new Error("ClientSecretCredential: tenantId, clientId, and clientSecret are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
3203
+ if (!tenantId) {
3204
+ throw new CredentialUnavailableError("ClientSecretCredential: tenantId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
3205
+ }
3206
+ if (!clientId) {
3207
+ throw new CredentialUnavailableError("ClientSecretCredential: clientId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
3208
+ }
3209
+ if (!clientSecret) {
3210
+ throw new CredentialUnavailableError("ClientSecretCredential: clientSecret is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.");
3134
3211
  }
3135
3212
  this.clientSecret = clientSecret;
3136
3213
  this.tenantId = tenantId;
@@ -3176,8 +3253,17 @@ class UsernamePasswordCredential {
3176
3253
  * @param options - Options for configuring the client which makes the authentication request.
3177
3254
  */
3178
3255
  constructor(tenantId, clientId, username, password, options = {}) {
3179
- if (!tenantId || !clientId || !username || !password) {
3180
- throw new Error("UsernamePasswordCredential: tenantId, clientId, username and password are required parameters. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
3256
+ if (!tenantId) {
3257
+ throw new CredentialUnavailableError("UsernamePasswordCredential: tenantId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
3258
+ }
3259
+ if (!clientId) {
3260
+ throw new CredentialUnavailableError("UsernamePasswordCredential: clientId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
3261
+ }
3262
+ if (!username) {
3263
+ throw new CredentialUnavailableError("UsernamePasswordCredential: username is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
3264
+ }
3265
+ if (!password) {
3266
+ throw new CredentialUnavailableError("UsernamePasswordCredential: password is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/usernamepasswordcredential/troubleshoot.");
3181
3267
  }
3182
3268
  this.tenantId = tenantId;
3183
3269
  this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
@@ -3224,6 +3310,7 @@ const AllSupportedEnvironmentVariables = [
3224
3310
  "AZURE_USERNAME",
3225
3311
  "AZURE_PASSWORD",
3226
3312
  "AZURE_ADDITIONALLY_ALLOWED_TENANTS",
3313
+ "AZURE_CLIENT_SEND_CERTIFICATE_CHAIN",
3227
3314
  ];
3228
3315
  function getAdditionallyAllowedTenants() {
3229
3316
  var _a;
@@ -3232,6 +3319,13 @@ function getAdditionallyAllowedTenants() {
3232
3319
  }
3233
3320
  const credentialName$2 = "EnvironmentCredential";
3234
3321
  const logger$6 = credentialLogger(credentialName$2);
3322
+ function getSendCertificateChain() {
3323
+ var _a;
3324
+ const sendCertificateChain = ((_a = process.env.AZURE_CLIENT_SEND_CERTIFICATE_CHAIN) !== null && _a !== void 0 ? _a : "").toLowerCase();
3325
+ const result = sendCertificateChain === "true" || sendCertificateChain === "1";
3326
+ logger$6.verbose(`AZURE_CLIENT_SEND_CERTIFICATE_CHAIN: ${process.env.AZURE_CLIENT_SEND_CERTIFICATE_CHAIN}; sendCertificateChain: ${result}`);
3327
+ return result;
3328
+ }
3235
3329
  /**
3236
3330
  * Enables authentication to Microsoft Entra ID using a client secret or certificate, or as a user
3237
3331
  * with a username and password.
@@ -3251,6 +3345,7 @@ class EnvironmentCredential {
3251
3345
  * - `AZURE_CLIENT_SECRET`: A client secret that was generated for the App Registration.
3252
3346
  * - `AZURE_CLIENT_CERTIFICATE_PATH`: The path to a PEM certificate to use during the authentication, instead of the client secret.
3253
3347
  * - `AZURE_CLIENT_CERTIFICATE_PASSWORD`: (optional) password for the certificate file.
3348
+ * - `AZURE_CLIENT_SEND_CERTIFICATE_CHAIN`: (optional) indicates that the certificate chain should be set in x5c header to support subject name / issuer based authentication.
3254
3349
  *
3255
3350
  * Alternatively, users can provide environment variables for username and password authentication:
3256
3351
  * - `AZURE_USERNAME`: Username to authenticate with.
@@ -3268,7 +3363,8 @@ class EnvironmentCredential {
3268
3363
  logger$6.info(`Found the following environment variables: ${assigned}`);
3269
3364
  const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
3270
3365
  const additionallyAllowedTenantIds = getAdditionallyAllowedTenants();
3271
- const newOptions = Object.assign(Object.assign({}, options), { additionallyAllowedTenantIds });
3366
+ const sendCertificateChain = getSendCertificateChain();
3367
+ const newOptions = Object.assign(Object.assign({}, options), { additionallyAllowedTenantIds, sendCertificateChain });
3272
3368
  if (tenantId) {
3273
3369
  checkTenantId(logger$6, tenantId);
3274
3370
  }
@@ -3529,7 +3625,7 @@ class InteractiveBrowserCredential {
3529
3625
  * Authenticates with Microsoft Entra ID and returns an access token if successful.
3530
3626
  * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
3531
3627
  *
3532
- * If the token can't be retrieved silently, this method will require user interaction to retrieve the token.
3628
+ * If the token can't be retrieved silently, this method will always generate a challenge for the user.
3533
3629
  *
3534
3630
  * On Node.js, this credential has [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) enabled by default.
3535
3631
  * PKCE is a security feature that mitigates authentication code interception attacks.
@@ -3615,7 +3711,7 @@ class DeviceCodeCredential {
3615
3711
  * Authenticates with Microsoft Entra ID and returns an access token if successful.
3616
3712
  * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
3617
3713
  *
3618
- * If the token can't be retrieved silently, this method will require user interaction to retrieve the token.
3714
+ * If the token can't be retrieved silently, this method will always generate a challenge for the user.
3619
3715
  *
3620
3716
  * @param scopes - The list of scopes for which the token will have access.
3621
3717
  * @param options - The options used to configure any requests this
@@ -3649,8 +3745,17 @@ class AzurePipelinesCredential {
3649
3745
  * @param options - The identity client options to use for authentication.
3650
3746
  */
3651
3747
  constructor(tenantId, clientId, serviceConnectionId, systemAccessToken, options) {
3652
- if (!clientId || !tenantId || !serviceConnectionId || !systemAccessToken) {
3653
- throw new CredentialUnavailableError(`${credentialName$1}: is unavailable. tenantId, clientId, serviceConnectionId, and systemAccessToken are required parameters.`);
3748
+ if (!clientId) {
3749
+ throw new CredentialUnavailableError(`${credentialName$1}: is unavailable. clientId is a required parameter.`);
3750
+ }
3751
+ if (!tenantId) {
3752
+ throw new CredentialUnavailableError(`${credentialName$1}: is unavailable. tenantId is a required parameter.`);
3753
+ }
3754
+ if (!serviceConnectionId) {
3755
+ throw new CredentialUnavailableError(`${credentialName$1}: is unavailable. serviceConnectionId is a required parameter.`);
3756
+ }
3757
+ if (!systemAccessToken) {
3758
+ throw new CredentialUnavailableError(`${credentialName$1}: is unavailable. systemAccessToken is a required parameter.`);
3654
3759
  }
3655
3760
  this.identityClient = new IdentityClient(options);
3656
3761
  checkTenantId(logger$2, tenantId);
@@ -3703,30 +3808,45 @@ class AzurePipelinesCredential {
3703
3808
  }),
3704
3809
  });
3705
3810
  const response = await this.identityClient.sendRequest(request);
3706
- const text = response.bodyAsText;
3707
- if (!text) {
3708
- logger$2.error(`${credentialName$1}: Authenticated Failed. Received null token from OIDC request. Response status- ${response.status}. Complete response - ${JSON.stringify(response)}`);
3709
- throw new AuthenticationError(response.status, `${credentialName$1}: Authenticated Failed. Received null token from OIDC request. Response status- ${response.status}. Complete response - ${JSON.stringify(response)}`);
3811
+ return handleOidcResponse(response);
3812
+ }
3813
+ }
3814
+ function handleOidcResponse(response) {
3815
+ const text = response.bodyAsText;
3816
+ if (!text) {
3817
+ logger$2.error(`${credentialName$1}: Authentication Failed. Received null token from OIDC request. Response status- ${response.status}. Complete response - ${JSON.stringify(response)}`);
3818
+ throw new AuthenticationError(response.status, {
3819
+ error: `${credentialName$1}: Authentication Failed. Received null token from OIDC request.`,
3820
+ error_description: `${JSON.stringify(response)}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`,
3821
+ });
3822
+ }
3823
+ try {
3824
+ const result = JSON.parse(text);
3825
+ if (result === null || result === void 0 ? void 0 : result.oidcToken) {
3826
+ return result.oidcToken;
3710
3827
  }
3711
- try {
3712
- const result = JSON.parse(text);
3713
- if (result === null || result === void 0 ? void 0 : result.oidcToken) {
3714
- return result.oidcToken;
3715
- }
3716
- else {
3717
- let errorMessage = `${credentialName$1}: Authentication Failed. oidcToken field not detected in the response.`;
3718
- if (response.status !== 200) {
3719
- errorMessage += `Response = ${JSON.stringify(result)}`;
3720
- }
3721
- logger$2.error(errorMessage);
3722
- throw new AuthenticationError(response.status, errorMessage);
3828
+ else {
3829
+ const errorMessage = `${credentialName$1}: Authentication Failed. oidcToken field not detected in the response.`;
3830
+ let errorDescription = ``;
3831
+ if (response.status !== 200) {
3832
+ errorDescription = `Complete response - ${JSON.stringify(result)}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`;
3723
3833
  }
3834
+ logger$2.error(errorMessage);
3835
+ logger$2.error(errorDescription);
3836
+ throw new AuthenticationError(response.status, {
3837
+ error: errorMessage,
3838
+ error_description: errorDescription,
3839
+ });
3724
3840
  }
3725
- catch (e) {
3726
- logger$2.error(e.message);
3727
- logger$2.error(`${credentialName$1}: Authentication Failed. oidcToken field not detected in the response. Response = ${text}`);
3728
- throw new AuthenticationError(response.status, `${credentialName$1}: Authentication Failed. oidcToken field not detected in the response. Response = ${text}`);
3729
- }
3841
+ }
3842
+ catch (e) {
3843
+ const errorDetails = `${credentialName$1}: Authentication Failed. oidcToken field not detected in the response.`;
3844
+ logger$2.error(`Response from service = ${text} and error message = ${e.message}`);
3845
+ logger$2.error(errorDetails);
3846
+ throw new AuthenticationError(response.status, {
3847
+ error: errorDetails,
3848
+ error_description: `Response = ${text}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`,
3849
+ });
3730
3850
  }
3731
3851
  }
3732
3852
 
@@ -3797,11 +3917,17 @@ class OnBehalfOfCredential {
3797
3917
  const { certificatePath, sendCertificateChain } = options;
3798
3918
  const { getAssertion } = options;
3799
3919
  const { tenantId, clientId, userAssertionToken, additionallyAllowedTenants: additionallyAllowedTenantIds, } = options;
3800
- if (!tenantId ||
3801
- !clientId ||
3802
- !(clientSecret || certificatePath || getAssertion) ||
3803
- !userAssertionToken) {
3804
- throw new Error(`${credentialName}: tenantId, clientId, clientSecret (or certificatePath or getAssertion) and userAssertionToken are required parameters.`);
3920
+ if (!tenantId) {
3921
+ throw new CredentialUnavailableError(`${credentialName}: tenantId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
3922
+ }
3923
+ if (!clientId) {
3924
+ throw new CredentialUnavailableError(`${credentialName}: clientId is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
3925
+ }
3926
+ if (!clientSecret && !certificatePath && !getAssertion) {
3927
+ throw new CredentialUnavailableError(`${credentialName}: You must provide one of clientSecret, certificatePath, or a getAssertion callback but none were provided. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
3928
+ }
3929
+ if (!userAssertionToken) {
3930
+ throw new CredentialUnavailableError(`${credentialName}: userAssertionToken is a required parameter. To troubleshoot, visit https://aka.ms/azsdk/js/identity/serviceprincipalauthentication/troubleshoot.`);
3805
3931
  }
3806
3932
  this.certificatePath = certificatePath;
3807
3933
  this.clientSecret = clientSecret;