@azure/identity 4.4.0 → 4.5.0-alpha.20240813.2
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.
- package/README.md +22 -1
- package/dist/index.js +441 -812
- package/dist/index.js.map +1 -1
- package/dist-esm/src/client/identityClient.js +8 -2
- package/dist-esm/src/client/identityClient.js.map +1 -1
- package/dist-esm/src/constants.js +1 -1
- package/dist-esm/src/constants.js.map +1 -1
- package/dist-esm/src/credentials/azurePipelinesCredential.js +50 -26
- package/dist-esm/src/credentials/azurePipelinesCredential.js.map +1 -1
- package/dist-esm/src/credentials/azurePowerShellCredential.js +63 -19
- package/dist-esm/src/credentials/azurePowerShellCredential.js.map +1 -1
- package/dist-esm/src/credentials/clientAssertionCredential.js +9 -2
- package/dist-esm/src/credentials/clientAssertionCredential.js.map +1 -1
- package/dist-esm/src/credentials/clientCertificateCredential.js +35 -27
- package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
- package/dist-esm/src/credentials/clientSecretCredential.js +9 -2
- package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
- package/dist-esm/src/credentials/deviceCodeCredential.js +1 -1
- package/dist-esm/src/credentials/deviceCodeCredential.js.map +1 -1
- package/dist-esm/src/credentials/deviceCodeCredentialOptions.js.map +1 -1
- package/dist-esm/src/credentials/environmentCredential.js +11 -1
- package/dist-esm/src/credentials/environmentCredential.js.map +1 -1
- package/dist-esm/src/credentials/interactiveBrowserCredential.browser.js.map +1 -1
- package/dist-esm/src/credentials/interactiveBrowserCredential.js +1 -1
- package/dist-esm/src/credentials/interactiveBrowserCredential.js.map +1 -1
- package/dist-esm/src/credentials/interactiveBrowserCredentialOptions.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/imdsRetryPolicy.js +37 -0
- package/dist-esm/src/credentials/managedIdentityCredential/imdsRetryPolicy.js.map +1 -0
- package/dist-esm/src/credentials/managedIdentityCredential/index.js +6 -2
- package/dist-esm/src/credentials/managedIdentityCredential/index.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/legacyMsiProvider.js +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/legacyMsiProvider.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/msalMsiProvider.js +195 -0
- package/dist-esm/src/credentials/managedIdentityCredential/msalMsiProvider.js.map +1 -0
- package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js +19 -22
- package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js.map +1 -1
- package/dist-esm/src/credentials/onBehalfOfCredential.js +16 -9
- package/dist-esm/src/credentials/onBehalfOfCredential.js.map +1 -1
- package/dist-esm/src/credentials/usernamePasswordCredential.js +13 -3
- package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -1
- package/dist-esm/src/credentials/workloadIdentityCredential.js +16 -6
- package/dist-esm/src/credentials/workloadIdentityCredential.js.map +1 -1
- package/dist-esm/src/errors.js +12 -7
- package/dist-esm/src/errors.js.map +1 -1
- package/dist-esm/src/msal/browserFlows/flows.js.map +1 -0
- package/dist-esm/src/msal/browserFlows/msalBrowserCommon.js.map +1 -1
- package/dist-esm/src/msal/nodeFlows/msalClient.js +8 -1
- package/dist-esm/src/msal/nodeFlows/msalClient.js.map +1 -1
- package/package.json +4 -4
- package/types/identity.d.ts +26 -8
- package/dist-esm/src/msal/flows.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js +0 -47
- package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js +0 -42
- package/dist-esm/src/msal/nodeFlows/msalClientAssertion.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js +0 -112
- package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalClientSecret.js +0 -33
- package/dist-esm/src/msal/nodeFlows/msalClientSecret.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js +0 -35
- package/dist-esm/src/msal/nodeFlows/msalDeviceCode.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js +0 -323
- package/dist-esm/src/msal/nodeFlows/msalNodeCommon.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js +0 -58
- package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js +0 -113
- package/dist-esm/src/msal/nodeFlows/msalOpenBrowser.js.map +0 -1
- package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js +0 -33
- package/dist-esm/src/msal/nodeFlows/msalUsernamePassword.js.map +0 -1
- /package/dist-esm/src/msal/{flows.js → browserFlows/flows.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,24 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var logger$
|
|
5
|
+
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
|
-
var fs$1 = require('node:fs');
|
|
16
|
-
var https = require('https');
|
|
17
15
|
var open = require('open');
|
|
18
16
|
var promises = require('fs/promises');
|
|
19
17
|
var child_process = require('child_process');
|
|
20
18
|
var crypto = require('crypto');
|
|
21
|
-
var promises$1 = require('node:fs/promises');
|
|
22
19
|
var node_crypto = require('node:crypto');
|
|
20
|
+
var promises$1 = require('node:fs/promises');
|
|
23
21
|
|
|
24
22
|
function _interopNamespaceDefault(e) {
|
|
25
23
|
var n = Object.create(null);
|
|
@@ -46,7 +44,7 @@ var child_process__namespace = /*#__PURE__*/_interopNamespaceDefault(child_proce
|
|
|
46
44
|
/**
|
|
47
45
|
* Current version of the `@azure/identity` package.
|
|
48
46
|
*/
|
|
49
|
-
const SDK_VERSION = `4.
|
|
47
|
+
const SDK_VERSION = `4.5.0-beta.2`;
|
|
50
48
|
/**
|
|
51
49
|
* The default client ID for authentication
|
|
52
50
|
* @internal
|
|
@@ -196,7 +194,7 @@ const msalPlugins = {
|
|
|
196
194
|
/**
|
|
197
195
|
* The AzureLogger used for all clients within the identity package
|
|
198
196
|
*/
|
|
199
|
-
const logger$
|
|
197
|
+
const logger$l = logger$m.createClientLogger("identity");
|
|
200
198
|
/**
|
|
201
199
|
* 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.
|
|
202
200
|
* @param supportedEnvVars - List of environment variable names
|
|
@@ -236,7 +234,7 @@ function formatError(scope, error) {
|
|
|
236
234
|
* `[title] => [message]`
|
|
237
235
|
*
|
|
238
236
|
*/
|
|
239
|
-
function credentialLoggerInstance(title, parent, log = logger$
|
|
237
|
+
function credentialLoggerInstance(title, parent, log = logger$l) {
|
|
240
238
|
const fullTitle = parent ? `${parent.fullTitle} ${title}` : title;
|
|
241
239
|
function info(message) {
|
|
242
240
|
log.info(`${fullTitle} =>`, message);
|
|
@@ -269,7 +267,7 @@ function credentialLoggerInstance(title, parent, log = logger$q) {
|
|
|
269
267
|
* `[title] => getToken() => [message]`
|
|
270
268
|
*
|
|
271
269
|
*/
|
|
272
|
-
function credentialLogger(title, log = logger$
|
|
270
|
+
function credentialLogger(title, log = logger$l) {
|
|
273
271
|
const credLogger = credentialLoggerInstance(title, undefined, log);
|
|
274
272
|
return Object.assign(Object.assign({}, credLogger), { parent: log, getToken: credentialLoggerInstance("=> getToken()", credLogger, log) });
|
|
275
273
|
}
|
|
@@ -291,8 +289,9 @@ const CredentialUnavailableErrorName = "CredentialUnavailableError";
|
|
|
291
289
|
* an error that should halt the chain, it's caught and the chain continues
|
|
292
290
|
*/
|
|
293
291
|
class CredentialUnavailableError extends Error {
|
|
294
|
-
constructor(message) {
|
|
295
|
-
|
|
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);
|
|
296
295
|
this.name = CredentialUnavailableErrorName;
|
|
297
296
|
}
|
|
298
297
|
}
|
|
@@ -307,7 +306,7 @@ const AuthenticationErrorName = "AuthenticationError";
|
|
|
307
306
|
*/
|
|
308
307
|
class AuthenticationError extends Error {
|
|
309
308
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
310
|
-
constructor(statusCode, errorBody) {
|
|
309
|
+
constructor(statusCode, errorBody, options) {
|
|
311
310
|
let errorResponse = {
|
|
312
311
|
error: "unknown",
|
|
313
312
|
errorDescription: "An unknown error occurred and no additional details are available.",
|
|
@@ -325,8 +324,8 @@ class AuthenticationError extends Error {
|
|
|
325
324
|
catch (e) {
|
|
326
325
|
if (statusCode === 400) {
|
|
327
326
|
errorResponse = {
|
|
328
|
-
error: "
|
|
329
|
-
errorDescription:
|
|
327
|
+
error: "invalid_request",
|
|
328
|
+
errorDescription: `The service indicated that the request was invalid.\n\n${errorBody}`,
|
|
330
329
|
};
|
|
331
330
|
}
|
|
332
331
|
else {
|
|
@@ -343,7 +342,9 @@ class AuthenticationError extends Error {
|
|
|
343
342
|
errorDescription: "An unknown error occurred and no additional details are available.",
|
|
344
343
|
};
|
|
345
344
|
}
|
|
346
|
-
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);
|
|
347
348
|
this.statusCode = statusCode;
|
|
348
349
|
this.errorResponse = errorResponse;
|
|
349
350
|
// Ensure that this type reports the correct name
|
|
@@ -386,7 +387,9 @@ class AuthenticationRequiredError extends Error {
|
|
|
386
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.
|
|
387
388
|
*/
|
|
388
389
|
options) {
|
|
389
|
-
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);
|
|
390
393
|
this.scopes = options.scopes;
|
|
391
394
|
this.getTokenOptions = options.getTokenOptions;
|
|
392
395
|
this.name = "AuthenticationRequiredError";
|
|
@@ -497,8 +500,6 @@ const DefaultScopeSuffix = "/.default";
|
|
|
497
500
|
const imdsHost = "http://169.254.169.254";
|
|
498
501
|
const imdsEndpointPath = "/metadata/identity/oauth2/token";
|
|
499
502
|
const imdsApiVersion = "2018-02-01";
|
|
500
|
-
const azureArcAPIVersion = "2019-11-01";
|
|
501
|
-
const azureFabricVersion = "2019-07-01-preview";
|
|
502
503
|
|
|
503
504
|
// Copyright (c) Microsoft Corporation.
|
|
504
505
|
// Licensed under the MIT license.
|
|
@@ -590,14 +591,19 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
590
591
|
} }, options), { userAgentOptions: {
|
|
591
592
|
userAgentPrefix,
|
|
592
593
|
}, baseUri }));
|
|
594
|
+
this.allowInsecureConnection = false;
|
|
593
595
|
this.authorityHost = baseUri;
|
|
594
596
|
this.abortControllers = new Map();
|
|
595
597
|
this.allowLoggingAccountIdentifiers = (_b = options === null || options === void 0 ? void 0 : options.loggingOptions) === null || _b === void 0 ? void 0 : _b.allowLoggingAccountIdentifiers;
|
|
596
598
|
// used for WorkloadIdentity
|
|
597
599
|
this.tokenCredentialOptions = Object.assign({}, options);
|
|
600
|
+
// used for ManagedIdentity
|
|
601
|
+
if (options === null || options === void 0 ? void 0 : options.allowInsecureConnection) {
|
|
602
|
+
this.allowInsecureConnection = options.allowInsecureConnection;
|
|
603
|
+
}
|
|
598
604
|
}
|
|
599
605
|
async sendTokenRequest(request) {
|
|
600
|
-
logger$
|
|
606
|
+
logger$l.info(`IdentityClient: sending token request to [${request.url}]`);
|
|
601
607
|
const response = await this.sendRequest(request);
|
|
602
608
|
if (response.bodyAsText && (response.status === 200 || response.status === 201)) {
|
|
603
609
|
const parsedBody = JSON.parse(response.bodyAsText);
|
|
@@ -612,12 +618,12 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
612
618
|
},
|
|
613
619
|
refreshToken: parsedBody.refresh_token,
|
|
614
620
|
};
|
|
615
|
-
logger$
|
|
621
|
+
logger$l.info(`IdentityClient: [${request.url}] token acquired, expires on ${token.accessToken.expiresOnTimestamp}`);
|
|
616
622
|
return token;
|
|
617
623
|
}
|
|
618
624
|
else {
|
|
619
625
|
const error = new AuthenticationError(response.status, response.bodyAsText);
|
|
620
|
-
logger$
|
|
626
|
+
logger$l.warning(`IdentityClient: authentication error. HTTP status: ${response.status}, ${error.errorResponse.errorDescription}`);
|
|
621
627
|
throw error;
|
|
622
628
|
}
|
|
623
629
|
}
|
|
@@ -625,7 +631,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
625
631
|
if (refreshToken === undefined) {
|
|
626
632
|
return null;
|
|
627
633
|
}
|
|
628
|
-
logger$
|
|
634
|
+
logger$l.info(`IdentityClient: refreshing access token with client ID: ${clientId}, scopes: ${scopes} started`);
|
|
629
635
|
const refreshParams = {
|
|
630
636
|
grant_type: "refresh_token",
|
|
631
637
|
client_id: clientId,
|
|
@@ -651,7 +657,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
651
657
|
tracingOptions: updatedOptions.tracingOptions,
|
|
652
658
|
});
|
|
653
659
|
const response = await this.sendTokenRequest(request);
|
|
654
|
-
logger$
|
|
660
|
+
logger$l.info(`IdentityClient: refreshed token for client ID: ${clientId}`);
|
|
655
661
|
return response;
|
|
656
662
|
}
|
|
657
663
|
catch (err) {
|
|
@@ -660,11 +666,11 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
660
666
|
// It's likely that the refresh token has expired, so
|
|
661
667
|
// return null so that the credential implementation will
|
|
662
668
|
// initiate the authentication flow again.
|
|
663
|
-
logger$
|
|
669
|
+
logger$l.info(`IdentityClient: interaction required for client ID: ${clientId}`);
|
|
664
670
|
return null;
|
|
665
671
|
}
|
|
666
672
|
else {
|
|
667
|
-
logger$
|
|
673
|
+
logger$l.warning(`IdentityClient: failed refreshing token for client ID: ${clientId}: ${err}`);
|
|
668
674
|
throw err;
|
|
669
675
|
}
|
|
670
676
|
}
|
|
@@ -673,7 +679,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
673
679
|
// Here is a custom layer that allows us to abort requests that go through MSAL,
|
|
674
680
|
// since MSAL doesn't allow us to pass options all the way through.
|
|
675
681
|
generateAbortSignal(correlationId) {
|
|
676
|
-
const controller = new
|
|
682
|
+
const controller = new AbortController();
|
|
677
683
|
const controllers = this.abortControllers.get(correlationId) || [];
|
|
678
684
|
controllers.push(controller);
|
|
679
685
|
this.abortControllers.set(correlationId, controllers);
|
|
@@ -681,7 +687,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
681
687
|
controller.signal.onabort = (...params) => {
|
|
682
688
|
this.abortControllers.set(correlationId, undefined);
|
|
683
689
|
if (existingOnAbort) {
|
|
684
|
-
existingOnAbort(
|
|
690
|
+
existingOnAbort.apply(controller.signal, params);
|
|
685
691
|
}
|
|
686
692
|
};
|
|
687
693
|
return controller.signal;
|
|
@@ -712,6 +718,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
712
718
|
url,
|
|
713
719
|
method: "GET",
|
|
714
720
|
body: options === null || options === void 0 ? void 0 : options.body,
|
|
721
|
+
allowInsecureConnection: this.allowInsecureConnection,
|
|
715
722
|
headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers),
|
|
716
723
|
abortSignal: this.generateAbortSignal(noCorrelationId),
|
|
717
724
|
});
|
|
@@ -729,6 +736,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
729
736
|
method: "POST",
|
|
730
737
|
body: options === null || options === void 0 ? void 0 : options.body,
|
|
731
738
|
headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers),
|
|
739
|
+
allowInsecureConnection: this.allowInsecureConnection,
|
|
732
740
|
// MSAL doesn't send the correlation ID on the get requests.
|
|
733
741
|
abortSignal: this.generateAbortSignal(this.getCorrelationId(options)),
|
|
734
742
|
});
|
|
@@ -773,10 +781,10 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
773
781
|
}
|
|
774
782
|
const base64Metadata = accessToken.split(".")[1];
|
|
775
783
|
const { appid, upn, tid, oid } = JSON.parse(Buffer.from(base64Metadata, "base64").toString("utf8"));
|
|
776
|
-
logger$
|
|
784
|
+
logger$l.info(`[Authenticated account] Client ID: ${appid}. Tenant ID: ${tid}. User Principal Name: ${upn || unavailableUpn}. Object ID (user): ${oid}`);
|
|
777
785
|
}
|
|
778
786
|
catch (e) {
|
|
779
|
-
logger$
|
|
787
|
+
logger$l.warning("allowLoggingAccountIdentifiers was set, but we couldn't log the account information. Error:", e.message);
|
|
780
788
|
}
|
|
781
789
|
}
|
|
782
790
|
}
|
|
@@ -785,7 +793,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
785
793
|
// Licensed under the MIT license.
|
|
786
794
|
const CommonTenantId = "common";
|
|
787
795
|
const AzureAccountClientId = "aebc6443-996d-45c2-90f0-388ff96faa56"; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56'
|
|
788
|
-
const logger$
|
|
796
|
+
const logger$k = credentialLogger("VisualStudioCodeCredential");
|
|
789
797
|
let findCredentials = undefined;
|
|
790
798
|
const vsCodeCredentialControl = {
|
|
791
799
|
setVsCodeCredentialFinder(finder) {
|
|
@@ -838,7 +846,7 @@ function getPropertyFromVSCode(property) {
|
|
|
838
846
|
}
|
|
839
847
|
}
|
|
840
848
|
catch (e) {
|
|
841
|
-
logger$
|
|
849
|
+
logger$k.info(`Failed to load the Visual Studio Code configuration file. Error: ${e.message}`);
|
|
842
850
|
return;
|
|
843
851
|
}
|
|
844
852
|
}
|
|
@@ -871,7 +879,7 @@ class VisualStudioCodeCredential {
|
|
|
871
879
|
const authorityHost = mapVSCodeAuthorityHosts[this.cloudName];
|
|
872
880
|
this.identityClient = new IdentityClient(Object.assign({ authorityHost }, options));
|
|
873
881
|
if (options && options.tenantId) {
|
|
874
|
-
checkTenantId(logger$
|
|
882
|
+
checkTenantId(logger$k, options.tenantId);
|
|
875
883
|
this.tenantId = options.tenantId;
|
|
876
884
|
}
|
|
877
885
|
else {
|
|
@@ -911,7 +919,7 @@ class VisualStudioCodeCredential {
|
|
|
911
919
|
async getToken(scopes, options) {
|
|
912
920
|
var _a, _b;
|
|
913
921
|
await this.prepareOnce();
|
|
914
|
-
const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds, logger$
|
|
922
|
+
const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds, logger$k) || this.tenantId;
|
|
915
923
|
if (findCredentials === undefined) {
|
|
916
924
|
throw new CredentialUnavailableError([
|
|
917
925
|
"No implementation of `VisualStudioCodeCredential` is available.",
|
|
@@ -925,7 +933,7 @@ class VisualStudioCodeCredential {
|
|
|
925
933
|
// Check to make sure the scope we get back is a valid scope
|
|
926
934
|
if (!scopeString.match(/^[0-9a-zA-Z-.:/]+$/)) {
|
|
927
935
|
const error = new Error("Invalid scope was specified by the user or calling client");
|
|
928
|
-
logger$
|
|
936
|
+
logger$k.getToken.info(formatError(scopes, error));
|
|
929
937
|
throw error;
|
|
930
938
|
}
|
|
931
939
|
if (scopeString.indexOf("offline_access") < 0) {
|
|
@@ -945,18 +953,18 @@ class VisualStudioCodeCredential {
|
|
|
945
953
|
if (refreshToken) {
|
|
946
954
|
const tokenResponse = await this.identityClient.refreshAccessToken(tenantId, AzureAccountClientId, scopeString, refreshToken, undefined);
|
|
947
955
|
if (tokenResponse) {
|
|
948
|
-
logger$
|
|
956
|
+
logger$k.getToken.info(formatSuccess(scopes));
|
|
949
957
|
return tokenResponse.accessToken;
|
|
950
958
|
}
|
|
951
959
|
else {
|
|
952
960
|
const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Have you connected using the 'Azure Account' extension recently? To troubleshoot, visit https://aka.ms/azsdk/js/identity/vscodecredential/troubleshoot.");
|
|
953
|
-
logger$
|
|
961
|
+
logger$k.getToken.info(formatError(scopes, error));
|
|
954
962
|
throw error;
|
|
955
963
|
}
|
|
956
964
|
}
|
|
957
965
|
else {
|
|
958
966
|
const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Did you connect using the 'Azure Account' extension? To troubleshoot, visit https://aka.ms/azsdk/js/identity/vscodecredential/troubleshoot.");
|
|
959
|
-
logger$
|
|
967
|
+
logger$k.getToken.info(formatError(scopes, error));
|
|
960
968
|
throw error;
|
|
961
969
|
}
|
|
962
970
|
}
|
|
@@ -1005,438 +1013,6 @@ function useIdentityPlugin(plugin) {
|
|
|
1005
1013
|
plugin(pluginContext);
|
|
1006
1014
|
}
|
|
1007
1015
|
|
|
1008
|
-
// Copyright (c) Microsoft Corporation.
|
|
1009
|
-
// Licensed under the MIT license.
|
|
1010
|
-
const msiName$6 = "ManagedIdentityCredential - CloudShellMSI";
|
|
1011
|
-
const logger$o = credentialLogger(msiName$6);
|
|
1012
|
-
/**
|
|
1013
|
-
* Generates the options used on the request for an access token.
|
|
1014
|
-
*/
|
|
1015
|
-
function prepareRequestOptions$5(scopes, clientId, resourceId) {
|
|
1016
|
-
const resource = mapScopesToResource(scopes);
|
|
1017
|
-
if (!resource) {
|
|
1018
|
-
throw new Error(`${msiName$6}: Multiple scopes are not supported.`);
|
|
1019
|
-
}
|
|
1020
|
-
const body = {
|
|
1021
|
-
resource,
|
|
1022
|
-
};
|
|
1023
|
-
if (clientId) {
|
|
1024
|
-
body.client_id = clientId;
|
|
1025
|
-
}
|
|
1026
|
-
if (resourceId) {
|
|
1027
|
-
body.msi_res_id = resourceId;
|
|
1028
|
-
}
|
|
1029
|
-
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
1030
|
-
if (!process.env.MSI_ENDPOINT) {
|
|
1031
|
-
throw new Error(`${msiName$6}: Missing environment variable: MSI_ENDPOINT`);
|
|
1032
|
-
}
|
|
1033
|
-
const params = new URLSearchParams(body);
|
|
1034
|
-
return {
|
|
1035
|
-
url: process.env.MSI_ENDPOINT,
|
|
1036
|
-
method: "POST",
|
|
1037
|
-
body: params.toString(),
|
|
1038
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
1039
|
-
Accept: "application/json",
|
|
1040
|
-
Metadata: "true",
|
|
1041
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
1042
|
-
}),
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Defines how to determine whether the Azure Cloud Shell MSI is available, and also how to retrieve a token from the Azure Cloud Shell MSI.
|
|
1047
|
-
* Since Azure Managed Identities aren't available in the Azure Cloud Shell, we log a warning for users that try to access cloud shell using user assigned identity.
|
|
1048
|
-
*/
|
|
1049
|
-
const cloudShellMsi = {
|
|
1050
|
-
name: "cloudShellMsi",
|
|
1051
|
-
async isAvailable({ scopes }) {
|
|
1052
|
-
const resource = mapScopesToResource(scopes);
|
|
1053
|
-
if (!resource) {
|
|
1054
|
-
logger$o.info(`${msiName$6}: Unavailable. Multiple scopes are not supported.`);
|
|
1055
|
-
return false;
|
|
1056
|
-
}
|
|
1057
|
-
const result = Boolean(process.env.MSI_ENDPOINT);
|
|
1058
|
-
if (!result) {
|
|
1059
|
-
logger$o.info(`${msiName$6}: Unavailable. The environment variable MSI_ENDPOINT is needed.`);
|
|
1060
|
-
}
|
|
1061
|
-
return result;
|
|
1062
|
-
},
|
|
1063
|
-
async getToken(configuration, getTokenOptions = {}) {
|
|
1064
|
-
const { identityClient, scopes, clientId, resourceId } = configuration;
|
|
1065
|
-
if (clientId) {
|
|
1066
|
-
logger$o.warning(`${msiName$6}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
|
|
1067
|
-
}
|
|
1068
|
-
if (resourceId) {
|
|
1069
|
-
logger$o.warning(`${msiName$6}: user defined managed Identity by resource Id not supported. The argument resourceId might be ignored by the service.`);
|
|
1070
|
-
}
|
|
1071
|
-
logger$o.info(`${msiName$6}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`);
|
|
1072
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$5(scopes, clientId, resourceId)), {
|
|
1073
|
-
// Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
|
|
1074
|
-
allowInsecureConnection: true }));
|
|
1075
|
-
const tokenResponse = await identityClient.sendTokenRequest(request);
|
|
1076
|
-
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
1077
|
-
},
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
// Copyright (c) Microsoft Corporation.
|
|
1081
|
-
// Licensed under the MIT license.
|
|
1082
|
-
const msiName$5 = "ManagedIdentityCredential - AppServiceMSI 2017";
|
|
1083
|
-
const logger$n = credentialLogger(msiName$5);
|
|
1084
|
-
/**
|
|
1085
|
-
* Generates the options used on the request for an access token.
|
|
1086
|
-
*/
|
|
1087
|
-
function prepareRequestOptions$4(scopes, clientId) {
|
|
1088
|
-
const resource = mapScopesToResource(scopes);
|
|
1089
|
-
if (!resource) {
|
|
1090
|
-
throw new Error(`${msiName$5}: Multiple scopes are not supported.`);
|
|
1091
|
-
}
|
|
1092
|
-
const queryParameters = {
|
|
1093
|
-
resource,
|
|
1094
|
-
"api-version": "2017-09-01",
|
|
1095
|
-
};
|
|
1096
|
-
if (clientId) {
|
|
1097
|
-
queryParameters.clientid = clientId;
|
|
1098
|
-
}
|
|
1099
|
-
const query = new URLSearchParams(queryParameters);
|
|
1100
|
-
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
1101
|
-
if (!process.env.MSI_ENDPOINT) {
|
|
1102
|
-
throw new Error(`${msiName$5}: Missing environment variable: MSI_ENDPOINT`);
|
|
1103
|
-
}
|
|
1104
|
-
if (!process.env.MSI_SECRET) {
|
|
1105
|
-
throw new Error(`${msiName$5}: Missing environment variable: MSI_SECRET`);
|
|
1106
|
-
}
|
|
1107
|
-
return {
|
|
1108
|
-
url: `${process.env.MSI_ENDPOINT}?${query.toString()}`,
|
|
1109
|
-
method: "GET",
|
|
1110
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
1111
|
-
Accept: "application/json",
|
|
1112
|
-
secret: process.env.MSI_SECRET,
|
|
1113
|
-
}),
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
/**
|
|
1117
|
-
* Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
|
|
1118
|
-
*/
|
|
1119
|
-
const appServiceMsi2017 = {
|
|
1120
|
-
name: "appServiceMsi2017",
|
|
1121
|
-
async isAvailable({ scopes }) {
|
|
1122
|
-
const resource = mapScopesToResource(scopes);
|
|
1123
|
-
if (!resource) {
|
|
1124
|
-
logger$n.info(`${msiName$5}: Unavailable. Multiple scopes are not supported.`);
|
|
1125
|
-
return false;
|
|
1126
|
-
}
|
|
1127
|
-
const env = process.env;
|
|
1128
|
-
const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
|
|
1129
|
-
if (!result) {
|
|
1130
|
-
logger$n.info(`${msiName$5}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`);
|
|
1131
|
-
}
|
|
1132
|
-
return result;
|
|
1133
|
-
},
|
|
1134
|
-
async getToken(configuration, getTokenOptions = {}) {
|
|
1135
|
-
const { identityClient, scopes, clientId, resourceId } = configuration;
|
|
1136
|
-
if (resourceId) {
|
|
1137
|
-
logger$n.warning(`${msiName$5}: managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
|
|
1138
|
-
}
|
|
1139
|
-
logger$n.info(`${msiName$5}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
|
|
1140
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$4(scopes, clientId)), {
|
|
1141
|
-
// Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
|
|
1142
|
-
allowInsecureConnection: true }));
|
|
1143
|
-
const tokenResponse = await identityClient.sendTokenRequest(request);
|
|
1144
|
-
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
1145
|
-
},
|
|
1146
|
-
};
|
|
1147
|
-
|
|
1148
|
-
// Copyright (c) Microsoft Corporation.
|
|
1149
|
-
// Licensed under the MIT license.
|
|
1150
|
-
const msiName$4 = "ManagedIdentityCredential - AppServiceMSI 2019";
|
|
1151
|
-
const logger$m = credentialLogger(msiName$4);
|
|
1152
|
-
/**
|
|
1153
|
-
* Generates the options used on the request for an access token.
|
|
1154
|
-
*/
|
|
1155
|
-
function prepareRequestOptions$3(scopes, clientId, resourceId) {
|
|
1156
|
-
const resource = mapScopesToResource(scopes);
|
|
1157
|
-
if (!resource) {
|
|
1158
|
-
throw new Error(`${msiName$4}: Multiple scopes are not supported.`);
|
|
1159
|
-
}
|
|
1160
|
-
const queryParameters = {
|
|
1161
|
-
resource,
|
|
1162
|
-
"api-version": "2019-08-01",
|
|
1163
|
-
};
|
|
1164
|
-
if (clientId) {
|
|
1165
|
-
queryParameters.client_id = clientId;
|
|
1166
|
-
}
|
|
1167
|
-
if (resourceId) {
|
|
1168
|
-
queryParameters.mi_res_id = resourceId;
|
|
1169
|
-
}
|
|
1170
|
-
const query = new URLSearchParams(queryParameters);
|
|
1171
|
-
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
1172
|
-
if (!process.env.IDENTITY_ENDPOINT) {
|
|
1173
|
-
throw new Error(`${msiName$4}: Missing environment variable: IDENTITY_ENDPOINT`);
|
|
1174
|
-
}
|
|
1175
|
-
if (!process.env.IDENTITY_HEADER) {
|
|
1176
|
-
throw new Error(`${msiName$4}: Missing environment variable: IDENTITY_HEADER`);
|
|
1177
|
-
}
|
|
1178
|
-
return {
|
|
1179
|
-
url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
|
|
1180
|
-
method: "GET",
|
|
1181
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
1182
|
-
Accept: "application/json",
|
|
1183
|
-
"X-IDENTITY-HEADER": process.env.IDENTITY_HEADER,
|
|
1184
|
-
}),
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI.
|
|
1189
|
-
*/
|
|
1190
|
-
const appServiceMsi2019 = {
|
|
1191
|
-
name: "appServiceMsi2019",
|
|
1192
|
-
async isAvailable({ scopes }) {
|
|
1193
|
-
const resource = mapScopesToResource(scopes);
|
|
1194
|
-
if (!resource) {
|
|
1195
|
-
logger$m.info(`${msiName$4}: Unavailable. Multiple scopes are not supported.`);
|
|
1196
|
-
return false;
|
|
1197
|
-
}
|
|
1198
|
-
const env = process.env;
|
|
1199
|
-
const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER);
|
|
1200
|
-
if (!result) {
|
|
1201
|
-
logger$m.info(`${msiName$4}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT and IDENTITY_HEADER.`);
|
|
1202
|
-
}
|
|
1203
|
-
return result;
|
|
1204
|
-
},
|
|
1205
|
-
async getToken(configuration, getTokenOptions = {}) {
|
|
1206
|
-
const { identityClient, scopes, clientId, resourceId } = configuration;
|
|
1207
|
-
logger$m.info(`${msiName$4}: Using the endpoint and the secret coming form the environment variables: IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT} and IDENTITY_HEADER=[REDACTED].`);
|
|
1208
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes, clientId, resourceId)), {
|
|
1209
|
-
// Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
|
|
1210
|
-
allowInsecureConnection: true }));
|
|
1211
|
-
const tokenResponse = await identityClient.sendTokenRequest(request);
|
|
1212
|
-
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
1213
|
-
},
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
// Copyright (c) Microsoft Corporation.
|
|
1217
|
-
// Licensed under the MIT license.
|
|
1218
|
-
const msiName$3 = "ManagedIdentityCredential - Azure Arc MSI";
|
|
1219
|
-
const logger$l = credentialLogger(msiName$3);
|
|
1220
|
-
/**
|
|
1221
|
-
* Generates the options used on the request for an access token.
|
|
1222
|
-
*/
|
|
1223
|
-
function prepareRequestOptions$2(scopes, clientId, resourceId) {
|
|
1224
|
-
const resource = mapScopesToResource(scopes);
|
|
1225
|
-
if (!resource) {
|
|
1226
|
-
throw new Error(`${msiName$3}: Multiple scopes are not supported.`);
|
|
1227
|
-
}
|
|
1228
|
-
const queryParameters = {
|
|
1229
|
-
resource,
|
|
1230
|
-
"api-version": azureArcAPIVersion,
|
|
1231
|
-
};
|
|
1232
|
-
if (clientId) {
|
|
1233
|
-
queryParameters.client_id = clientId;
|
|
1234
|
-
}
|
|
1235
|
-
if (resourceId) {
|
|
1236
|
-
queryParameters.msi_res_id = resourceId;
|
|
1237
|
-
}
|
|
1238
|
-
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
1239
|
-
if (!process.env.IDENTITY_ENDPOINT) {
|
|
1240
|
-
throw new Error(`${msiName$3}: Missing environment variable: IDENTITY_ENDPOINT`);
|
|
1241
|
-
}
|
|
1242
|
-
const query = new URLSearchParams(queryParameters);
|
|
1243
|
-
return coreRestPipeline.createPipelineRequest({
|
|
1244
|
-
// Should be similar to: http://localhost:40342/metadata/identity/oauth2/token
|
|
1245
|
-
url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
|
|
1246
|
-
method: "GET",
|
|
1247
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
1248
|
-
Accept: "application/json",
|
|
1249
|
-
Metadata: "true",
|
|
1250
|
-
}),
|
|
1251
|
-
});
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Does a request to the authentication provider that results in a file path.
|
|
1255
|
-
*/
|
|
1256
|
-
async function filePathRequest(identityClient, requestPrepareOptions) {
|
|
1257
|
-
const response = await identityClient.sendRequest(coreRestPipeline.createPipelineRequest(requestPrepareOptions));
|
|
1258
|
-
if (response.status !== 401) {
|
|
1259
|
-
let message = "";
|
|
1260
|
-
if (response.bodyAsText) {
|
|
1261
|
-
message = ` Response: ${response.bodyAsText}`;
|
|
1262
|
-
}
|
|
1263
|
-
throw new AuthenticationError(response.status, `${msiName$3}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`);
|
|
1264
|
-
}
|
|
1265
|
-
const authHeader = response.headers.get("www-authenticate") || "";
|
|
1266
|
-
try {
|
|
1267
|
-
return authHeader.split("=").slice(1)[0];
|
|
1268
|
-
}
|
|
1269
|
-
catch (e) {
|
|
1270
|
-
throw Error(`Invalid www-authenticate header format: ${authHeader}`);
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
function platformToFilePath() {
|
|
1274
|
-
switch (process.platform) {
|
|
1275
|
-
case "win32":
|
|
1276
|
-
if (!process.env.PROGRAMDATA) {
|
|
1277
|
-
throw new Error(`${msiName$3}: PROGRAMDATA environment variable has no value.`);
|
|
1278
|
-
}
|
|
1279
|
-
return `${process.env.PROGRAMDATA}\\AzureConnectedMachineAgent\\Tokens`;
|
|
1280
|
-
case "linux":
|
|
1281
|
-
return "/var/opt/azcmagent/tokens";
|
|
1282
|
-
default:
|
|
1283
|
-
throw new Error(`${msiName$3}: Unsupported platform ${process.platform}.`);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
/**
|
|
1287
|
-
* Validates that a given Azure Arc MSI file path is valid for use.
|
|
1288
|
-
*
|
|
1289
|
-
* A valid file will:
|
|
1290
|
-
* 1. Be in the expected path for the current platform.
|
|
1291
|
-
* 2. Have a `.key` extension.
|
|
1292
|
-
* 3. Be at most 4096 bytes in size.
|
|
1293
|
-
*/
|
|
1294
|
-
function validateKeyFile(filePath) {
|
|
1295
|
-
if (!filePath) {
|
|
1296
|
-
throw new Error(`${msiName$3}: Failed to find the token file.`);
|
|
1297
|
-
}
|
|
1298
|
-
if (!filePath.endsWith(".key")) {
|
|
1299
|
-
throw new Error(`${msiName$3}: unexpected file path from HIMDS service: ${filePath}.`);
|
|
1300
|
-
}
|
|
1301
|
-
const expectedPath = platformToFilePath();
|
|
1302
|
-
if (!filePath.startsWith(expectedPath)) {
|
|
1303
|
-
throw new Error(`${msiName$3}: unexpected file path from HIMDS service: ${filePath}.`);
|
|
1304
|
-
}
|
|
1305
|
-
const stats = fs$1.statSync(filePath);
|
|
1306
|
-
if (stats.size > 4096) {
|
|
1307
|
-
throw new Error(`${msiName$3}: The file at ${filePath} is larger than expected at ${stats.size} bytes.`);
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Defines how to determine whether the Azure Arc MSI is available, and also how to retrieve a token from the Azure Arc MSI.
|
|
1312
|
-
*/
|
|
1313
|
-
const arcMsi = {
|
|
1314
|
-
name: "arc",
|
|
1315
|
-
async isAvailable({ scopes }) {
|
|
1316
|
-
const resource = mapScopesToResource(scopes);
|
|
1317
|
-
if (!resource) {
|
|
1318
|
-
logger$l.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`);
|
|
1319
|
-
return false;
|
|
1320
|
-
}
|
|
1321
|
-
const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
|
|
1322
|
-
if (!result) {
|
|
1323
|
-
logger$l.info(`${msiName$3}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`);
|
|
1324
|
-
}
|
|
1325
|
-
return result;
|
|
1326
|
-
},
|
|
1327
|
-
async getToken(configuration, getTokenOptions = {}) {
|
|
1328
|
-
var _a;
|
|
1329
|
-
const { identityClient, scopes, clientId, resourceId } = configuration;
|
|
1330
|
-
if (clientId) {
|
|
1331
|
-
logger$l.warning(`${msiName$3}: user-assigned identities not supported. The argument clientId might be ignored by the service.`);
|
|
1332
|
-
}
|
|
1333
|
-
if (resourceId) {
|
|
1334
|
-
logger$l.warning(`${msiName$3}: user defined managed Identity by resource Id is not supported. Argument resourceId will be ignored.`);
|
|
1335
|
-
}
|
|
1336
|
-
logger$l.info(`${msiName$3}: Authenticating.`);
|
|
1337
|
-
const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$2(scopes, clientId, resourceId)), { allowInsecureConnection: true });
|
|
1338
|
-
const filePath = await filePathRequest(identityClient, requestOptions);
|
|
1339
|
-
validateKeyFile(filePath);
|
|
1340
|
-
const key = await fs$1.promises.readFile(filePath, { encoding: "utf-8" });
|
|
1341
|
-
(_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`);
|
|
1342
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({}, requestOptions), {
|
|
1343
|
-
// Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS).
|
|
1344
|
-
allowInsecureConnection: true }));
|
|
1345
|
-
const tokenResponse = await identityClient.sendTokenRequest(request);
|
|
1346
|
-
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
1347
|
-
},
|
|
1348
|
-
};
|
|
1349
|
-
|
|
1350
|
-
// Copyright (c) Microsoft Corporation.
|
|
1351
|
-
// Licensed under the MIT license.
|
|
1352
|
-
// This MSI can be easily tested by deploying a container to Azure Service Fabric with the Dockerfile:
|
|
1353
|
-
//
|
|
1354
|
-
// FROM node:12
|
|
1355
|
-
// RUN wget https://host.any/path/bash.sh
|
|
1356
|
-
// CMD ["bash", "bash.sh"]
|
|
1357
|
-
//
|
|
1358
|
-
// Where the bash script contains:
|
|
1359
|
-
//
|
|
1360
|
-
// curl --insecure $IDENTITY_ENDPOINT'?api-version=2019-07-01-preview&resource=https://vault.azure.net/' -H "Secret: $IDENTITY_HEADER"
|
|
1361
|
-
//
|
|
1362
|
-
const msiName$2 = "ManagedIdentityCredential - Fabric MSI";
|
|
1363
|
-
const logger$k = credentialLogger(msiName$2);
|
|
1364
|
-
/**
|
|
1365
|
-
* Generates the options used on the request for an access token.
|
|
1366
|
-
*/
|
|
1367
|
-
function prepareRequestOptions$1(scopes, clientId, resourceId) {
|
|
1368
|
-
const resource = mapScopesToResource(scopes);
|
|
1369
|
-
if (!resource) {
|
|
1370
|
-
throw new Error(`${msiName$2}: Multiple scopes are not supported.`);
|
|
1371
|
-
}
|
|
1372
|
-
const queryParameters = {
|
|
1373
|
-
resource,
|
|
1374
|
-
"api-version": azureFabricVersion,
|
|
1375
|
-
};
|
|
1376
|
-
if (clientId) {
|
|
1377
|
-
queryParameters.client_id = clientId;
|
|
1378
|
-
}
|
|
1379
|
-
if (resourceId) {
|
|
1380
|
-
queryParameters.msi_res_id = resourceId;
|
|
1381
|
-
}
|
|
1382
|
-
const query = new URLSearchParams(queryParameters);
|
|
1383
|
-
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
1384
|
-
if (!process.env.IDENTITY_ENDPOINT) {
|
|
1385
|
-
throw new Error("Missing environment variable: IDENTITY_ENDPOINT");
|
|
1386
|
-
}
|
|
1387
|
-
if (!process.env.IDENTITY_HEADER) {
|
|
1388
|
-
throw new Error("Missing environment variable: IDENTITY_HEADER");
|
|
1389
|
-
}
|
|
1390
|
-
return {
|
|
1391
|
-
url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`,
|
|
1392
|
-
method: "GET",
|
|
1393
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
1394
|
-
Accept: "application/json",
|
|
1395
|
-
secret: process.env.IDENTITY_HEADER,
|
|
1396
|
-
}),
|
|
1397
|
-
};
|
|
1398
|
-
}
|
|
1399
|
-
/**
|
|
1400
|
-
* Defines how to determine whether the Azure Service Fabric MSI is available, and also how to retrieve a token from the Azure Service Fabric MSI.
|
|
1401
|
-
*/
|
|
1402
|
-
const fabricMsi = {
|
|
1403
|
-
name: "fabricMsi",
|
|
1404
|
-
async isAvailable({ scopes }) {
|
|
1405
|
-
const resource = mapScopesToResource(scopes);
|
|
1406
|
-
if (!resource) {
|
|
1407
|
-
logger$k.info(`${msiName$2}: Unavailable. Multiple scopes are not supported.`);
|
|
1408
|
-
return false;
|
|
1409
|
-
}
|
|
1410
|
-
const env = process.env;
|
|
1411
|
-
const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER && env.IDENTITY_SERVER_THUMBPRINT);
|
|
1412
|
-
if (!result) {
|
|
1413
|
-
logger$k.info(`${msiName$2}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT, IDENTITY_HEADER and IDENTITY_SERVER_THUMBPRINT`);
|
|
1414
|
-
}
|
|
1415
|
-
return result;
|
|
1416
|
-
},
|
|
1417
|
-
async getToken(configuration, getTokenOptions = {}) {
|
|
1418
|
-
const { scopes, identityClient, clientId, resourceId } = configuration;
|
|
1419
|
-
if (resourceId) {
|
|
1420
|
-
logger$k.warning(`${msiName$2}: user defined managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`);
|
|
1421
|
-
}
|
|
1422
|
-
logger$k.info([
|
|
1423
|
-
`${msiName$2}:`,
|
|
1424
|
-
"Using the endpoint and the secret coming from the environment variables:",
|
|
1425
|
-
`IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT},`,
|
|
1426
|
-
"IDENTITY_HEADER=[REDACTED] and",
|
|
1427
|
-
"IDENTITY_SERVER_THUMBPRINT=[REDACTED].",
|
|
1428
|
-
].join(" "));
|
|
1429
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$1(scopes, clientId, resourceId)));
|
|
1430
|
-
request.agent = new https.Agent({
|
|
1431
|
-
// This is necessary because Service Fabric provides a self-signed certificate.
|
|
1432
|
-
// The alternative path is to verify the certificate using the IDENTITY_SERVER_THUMBPRINT env variable.
|
|
1433
|
-
rejectUnauthorized: false,
|
|
1434
|
-
});
|
|
1435
|
-
const tokenResponse = await identityClient.sendTokenRequest(request);
|
|
1436
|
-
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
1437
|
-
},
|
|
1438
|
-
};
|
|
1439
|
-
|
|
1440
1016
|
// Copyright (c) Microsoft Corporation.
|
|
1441
1017
|
// Licensed under the MIT license.
|
|
1442
1018
|
/**
|
|
@@ -1776,6 +1352,41 @@ const imdsMsi = {
|
|
|
1776
1352
|
},
|
|
1777
1353
|
};
|
|
1778
1354
|
|
|
1355
|
+
// Copyright (c) Microsoft Corporation.
|
|
1356
|
+
// Licensed under the MIT license.
|
|
1357
|
+
// Matches the default retry configuration in expontentialRetryStrategy.ts
|
|
1358
|
+
const DEFAULT_CLIENT_MAX_RETRY_INTERVAL = 1000 * 64;
|
|
1359
|
+
/**
|
|
1360
|
+
* An additional policy that retries on 404 errors. The default retry policy does not retry on
|
|
1361
|
+
* 404s, but the IMDS endpoint can return 404s when the token is not yet available. This policy
|
|
1362
|
+
* will retry on 404s with an exponential backoff.
|
|
1363
|
+
*
|
|
1364
|
+
* @param msiRetryConfig - The retry configuration for the MSI credential.
|
|
1365
|
+
* @returns - The policy that will retry on 404s.
|
|
1366
|
+
*/
|
|
1367
|
+
function imdsRetryPolicy(msiRetryConfig) {
|
|
1368
|
+
return coreRestPipeline.retryPolicy([
|
|
1369
|
+
{
|
|
1370
|
+
name: "imdsRetryPolicy",
|
|
1371
|
+
retry: ({ retryCount, response }) => {
|
|
1372
|
+
if ((response === null || response === void 0 ? void 0 : response.status) !== 404) {
|
|
1373
|
+
return { skipStrategy: true };
|
|
1374
|
+
}
|
|
1375
|
+
// Exponentially increase the delay each time
|
|
1376
|
+
const exponentialDelay = msiRetryConfig.startDelayInMs * Math.pow(2, retryCount);
|
|
1377
|
+
// Don't let the delay exceed the maximum
|
|
1378
|
+
const clampedExponentialDelay = Math.min(DEFAULT_CLIENT_MAX_RETRY_INTERVAL, exponentialDelay);
|
|
1379
|
+
// Allow the final value to have some "jitter" (within 50% of the delay size) so
|
|
1380
|
+
// that retries across multiple clients don't occur simultaneously.
|
|
1381
|
+
const retryAfterInMs = clampedExponentialDelay / 2 + coreUtil.getRandomIntegerInclusive(0, clampedExponentialDelay / 2);
|
|
1382
|
+
return { retryAfterInMs };
|
|
1383
|
+
},
|
|
1384
|
+
},
|
|
1385
|
+
], {
|
|
1386
|
+
maxRetries: msiRetryConfig.maxRetries,
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1779
1390
|
// Copyright (c) Microsoft Corporation.
|
|
1780
1391
|
// Licensed under the MIT license.
|
|
1781
1392
|
/**
|
|
@@ -1918,6 +1529,10 @@ function calculateRegionalAuthority(regionalAuthority) {
|
|
|
1918
1529
|
|
|
1919
1530
|
// Copyright (c) Microsoft Corporation.
|
|
1920
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");
|
|
1921
1536
|
/**
|
|
1922
1537
|
* A call to open(), but mockable
|
|
1923
1538
|
* @internal
|
|
@@ -1925,13 +1540,6 @@ function calculateRegionalAuthority(regionalAuthority) {
|
|
|
1925
1540
|
const interactiveBrowserMockable = {
|
|
1926
1541
|
open,
|
|
1927
1542
|
};
|
|
1928
|
-
|
|
1929
|
-
// Copyright (c) Microsoft Corporation.
|
|
1930
|
-
// Licensed under the MIT license.
|
|
1931
|
-
/**
|
|
1932
|
-
* The default logger used if no logger was passed in by the credential.
|
|
1933
|
-
*/
|
|
1934
|
-
const msalLogger = credentialLogger("MsalClient");
|
|
1935
1543
|
/**
|
|
1936
1544
|
* Generates the configuration for MSAL (Microsoft Authentication Library).
|
|
1937
1545
|
*
|
|
@@ -1956,7 +1564,7 @@ function generateMsalConfiguration(clientId, tenantId, msalClientOptions = {}) {
|
|
|
1956
1564
|
networkClient: httpClient,
|
|
1957
1565
|
loggerOptions: {
|
|
1958
1566
|
loggerCallback: defaultLoggerCallback((_c = msalClientOptions.logger) !== null && _c !== void 0 ? _c : msalLogger),
|
|
1959
|
-
logLevel: getMSALLogLevel(logger$
|
|
1567
|
+
logLevel: getMSALLogLevel(logger$m.getLogLevel()),
|
|
1960
1568
|
piiLoggingEnabled: (_d = msalClientOptions.loggingOptions) === null || _d === void 0 ? void 0 : _d.enableUnsafeSupportLogging,
|
|
1961
1569
|
},
|
|
1962
1570
|
},
|
|
@@ -2377,8 +1985,14 @@ class ClientAssertionCredential {
|
|
|
2377
1985
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
2378
1986
|
*/
|
|
2379
1987
|
constructor(tenantId, clientId, getAssertion, options = {}) {
|
|
2380
|
-
if (!tenantId
|
|
2381
|
-
throw new
|
|
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.");
|
|
2382
1996
|
}
|
|
2383
1997
|
this.tenantId = tenantId;
|
|
2384
1998
|
this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
|
|
@@ -2453,10 +2067,20 @@ class WorkloadIdentityCredential {
|
|
|
2453
2067
|
if (tenantId) {
|
|
2454
2068
|
checkTenantId(logger$g, tenantId);
|
|
2455
2069
|
}
|
|
2456
|
-
if (clientId
|
|
2457
|
-
|
|
2458
|
-
|
|
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`);
|
|
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`);
|
|
2459
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);
|
|
2460
2084
|
}
|
|
2461
2085
|
/**
|
|
2462
2086
|
* Authenticates with Microsoft Entra ID and returns an access token if successful.
|
|
@@ -2472,7 +2096,7 @@ class WorkloadIdentityCredential {
|
|
|
2472
2096
|
In DefaultAzureCredential and ManagedIdentityCredential, these can be provided as environment variables -
|
|
2473
2097
|
"AZURE_TENANT_ID",
|
|
2474
2098
|
"AZURE_CLIENT_ID",
|
|
2475
|
-
"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`;
|
|
2476
2100
|
logger$g.info(errorMessage);
|
|
2477
2101
|
throw new CredentialUnavailableError(errorMessage);
|
|
2478
2102
|
}
|
|
@@ -2509,132 +2133,76 @@ const logger$f = credentialLogger(msiName);
|
|
|
2509
2133
|
/**
|
|
2510
2134
|
* Defines how to determine whether the token exchange MSI is available, and also how to retrieve a token from the token exchange MSI.
|
|
2511
2135
|
*/
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
},
|
|
2532
|
-
};
|
|
2533
|
-
}
|
|
2136
|
+
const tokenExchangeMsi = {
|
|
2137
|
+
name: "tokenExchangeMsi",
|
|
2138
|
+
async isAvailable({ clientId }) {
|
|
2139
|
+
const env = process.env;
|
|
2140
|
+
const result = Boolean((clientId || env.AZURE_CLIENT_ID) &&
|
|
2141
|
+
env.AZURE_TENANT_ID &&
|
|
2142
|
+
process.env.AZURE_FEDERATED_TOKEN_FILE);
|
|
2143
|
+
if (!result) {
|
|
2144
|
+
logger$f.info(`${msiName}: 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`);
|
|
2145
|
+
}
|
|
2146
|
+
return result;
|
|
2147
|
+
},
|
|
2148
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2149
|
+
const { scopes, clientId } = configuration;
|
|
2150
|
+
const identityClientTokenCredentialOptions = {};
|
|
2151
|
+
const workloadIdentityCredential = new WorkloadIdentityCredential(Object.assign(Object.assign({ clientId, tenantId: process.env.AZURE_TENANT_ID, tokenFilePath: process.env.AZURE_FEDERATED_TOKEN_FILE }, identityClientTokenCredentialOptions), { disableInstanceDiscovery: true }));
|
|
2152
|
+
return workloadIdentityCredential.getToken(scopes, getTokenOptions);
|
|
2153
|
+
},
|
|
2154
|
+
};
|
|
2534
2155
|
|
|
2535
2156
|
// Copyright (c) Microsoft Corporation.
|
|
2536
2157
|
// Licensed under the MIT license.
|
|
2537
|
-
const logger$e = credentialLogger("ManagedIdentityCredential");
|
|
2538
|
-
class
|
|
2539
|
-
constructor(clientIdOrOptions, options) {
|
|
2158
|
+
const logger$e = credentialLogger("ManagedIdentityCredential(MSAL)");
|
|
2159
|
+
class MsalMsiProvider {
|
|
2160
|
+
constructor(clientIdOrOptions, options = {}) {
|
|
2540
2161
|
var _a, _b;
|
|
2541
|
-
this.isEndpointUnavailable = null;
|
|
2542
|
-
this.isAppTokenProviderInitialized = false;
|
|
2543
2162
|
this.msiRetryConfig = {
|
|
2544
2163
|
maxRetries: 5,
|
|
2545
2164
|
startDelayInMs: 800,
|
|
2546
2165
|
intervalIncrement: 2,
|
|
2547
2166
|
};
|
|
2548
|
-
let _options;
|
|
2167
|
+
let _options = {};
|
|
2549
2168
|
if (typeof clientIdOrOptions === "string") {
|
|
2550
2169
|
this.clientId = clientIdOrOptions;
|
|
2551
2170
|
_options = options;
|
|
2552
2171
|
}
|
|
2553
2172
|
else {
|
|
2554
2173
|
this.clientId = clientIdOrOptions === null || clientIdOrOptions === void 0 ? void 0 : clientIdOrOptions.clientId;
|
|
2555
|
-
_options = clientIdOrOptions;
|
|
2174
|
+
_options = clientIdOrOptions !== null && clientIdOrOptions !== void 0 ? clientIdOrOptions : {};
|
|
2556
2175
|
}
|
|
2557
2176
|
this.resourceId = _options === null || _options === void 0 ? void 0 : _options.resourceId;
|
|
2558
2177
|
// For JavaScript users.
|
|
2559
2178
|
if (this.clientId && this.resourceId) {
|
|
2560
2179
|
throw new Error(`ManagedIdentityCredential - Client Id and Resource Id can't be provided at the same time.`);
|
|
2561
2180
|
}
|
|
2181
|
+
// ManagedIdentity uses http for local requests
|
|
2182
|
+
_options.allowInsecureConnection = true;
|
|
2562
2183
|
if (((_a = _options === null || _options === void 0 ? void 0 : _options.retryOptions) === null || _a === void 0 ? void 0 : _a.maxRetries) !== undefined) {
|
|
2563
2184
|
this.msiRetryConfig.maxRetries = _options.retryOptions.maxRetries;
|
|
2564
2185
|
}
|
|
2565
|
-
this.identityClient = new IdentityClient(_options);
|
|
2566
|
-
this.
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
* since this wasn't done previously before adding token cache support
|
|
2571
|
-
*/
|
|
2572
|
-
this.confidentialApp = new msalCommon.ConfidentialClientApplication({
|
|
2573
|
-
auth: {
|
|
2574
|
-
authority: "https://login.microsoftonline.com/managed_identity",
|
|
2575
|
-
clientId: (_b = this.clientId) !== null && _b !== void 0 ? _b : DeveloperSignOnClientId,
|
|
2576
|
-
clientSecret: "dummy-secret",
|
|
2577
|
-
cloudDiscoveryMetadata: '{"tenant_discovery_endpoint":"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}',
|
|
2578
|
-
authorityMetadata: '{"token_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/common/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/{tenantid}/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"kerberos_endpoint":"https://login.microsoftonline.com/common/kerberos","tenant_region_scope":null,"cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}',
|
|
2579
|
-
clientCapabilities: [],
|
|
2186
|
+
this.identityClient = new IdentityClient(Object.assign(Object.assign({}, _options), { additionalPolicies: [{ policy: imdsRetryPolicy(this.msiRetryConfig), position: "perCall" }] }));
|
|
2187
|
+
this.managedIdentityApp = new msalCommon.ManagedIdentityApplication({
|
|
2188
|
+
managedIdentityIdParams: {
|
|
2189
|
+
userAssignedClientId: this.clientId,
|
|
2190
|
+
userAssignedResourceId: this.resourceId,
|
|
2580
2191
|
},
|
|
2581
2192
|
system: {
|
|
2193
|
+
// todo: proxyUrl?
|
|
2194
|
+
disableInternalRetries: true,
|
|
2195
|
+
networkClient: this.identityClient,
|
|
2582
2196
|
loggerOptions: {
|
|
2583
|
-
logLevel: getMSALLogLevel(logger$
|
|
2197
|
+
logLevel: getMSALLogLevel(logger$m.getLogLevel()),
|
|
2198
|
+
piiLoggingEnabled: (_b = options.loggingOptions) === null || _b === void 0 ? void 0 : _b.enableUnsafeSupportLogging,
|
|
2199
|
+
loggerCallback: defaultLoggerCallback(logger$e),
|
|
2584
2200
|
},
|
|
2585
2201
|
},
|
|
2586
2202
|
});
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
return this.cachedMSI;
|
|
2591
|
-
}
|
|
2592
|
-
const MSIs = [
|
|
2593
|
-
arcMsi,
|
|
2594
|
-
fabricMsi,
|
|
2595
|
-
appServiceMsi2019,
|
|
2596
|
-
appServiceMsi2017,
|
|
2597
|
-
cloudShellMsi,
|
|
2598
|
-
tokenExchangeMsi(),
|
|
2599
|
-
imdsMsi,
|
|
2600
|
-
];
|
|
2601
|
-
for (const msi of MSIs) {
|
|
2602
|
-
if (await msi.isAvailable({
|
|
2603
|
-
scopes,
|
|
2604
|
-
identityClient: this.isAvailableIdentityClient,
|
|
2605
|
-
clientId: this.clientId,
|
|
2606
|
-
resourceId: this.resourceId,
|
|
2607
|
-
getTokenOptions,
|
|
2608
|
-
})) {
|
|
2609
|
-
this.cachedMSI = msi;
|
|
2610
|
-
return msi;
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
throw new CredentialUnavailableError(`ManagedIdentityCredential - No MSI credential available`);
|
|
2614
|
-
}
|
|
2615
|
-
async authenticateManagedIdentity(scopes, getTokenOptions) {
|
|
2616
|
-
const { span, updatedOptions } = tracingClient.startSpan(`ManagedIdentityCredential.authenticateManagedIdentity`, getTokenOptions);
|
|
2617
|
-
try {
|
|
2618
|
-
// Determining the available MSI, and avoiding checking for other MSIs while the program is running.
|
|
2619
|
-
const availableMSI = await this.cachedAvailableMSI(scopes, updatedOptions);
|
|
2620
|
-
return availableMSI.getToken({
|
|
2621
|
-
identityClient: this.identityClient,
|
|
2622
|
-
scopes,
|
|
2623
|
-
clientId: this.clientId,
|
|
2624
|
-
resourceId: this.resourceId,
|
|
2625
|
-
retryConfig: this.msiRetryConfig,
|
|
2626
|
-
}, updatedOptions);
|
|
2627
|
-
}
|
|
2628
|
-
catch (err) {
|
|
2629
|
-
span.setStatus({
|
|
2630
|
-
status: "error",
|
|
2631
|
-
error: err,
|
|
2632
|
-
});
|
|
2633
|
-
throw err;
|
|
2634
|
-
}
|
|
2635
|
-
finally {
|
|
2636
|
-
span.end();
|
|
2637
|
-
}
|
|
2203
|
+
this.isAvailableIdentityClient = new IdentityClient(Object.assign(Object.assign({}, _options), { retryOptions: {
|
|
2204
|
+
maxRetries: 0,
|
|
2205
|
+
} }));
|
|
2638
2206
|
}
|
|
2639
2207
|
/**
|
|
2640
2208
|
* Authenticates with Microsoft Entra ID and returns an access token if successful.
|
|
@@ -2645,133 +2213,93 @@ class LegacyMsiProvider {
|
|
|
2645
2213
|
* @param options - The options used to configure any requests this
|
|
2646
2214
|
* TokenCredential implementation might make.
|
|
2647
2215
|
*/
|
|
2648
|
-
async getToken(scopes, options) {
|
|
2649
|
-
|
|
2650
|
-
const
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2216
|
+
async getToken(scopes, options = {}) {
|
|
2217
|
+
logger$e.getToken.info("Using the MSAL provider for Managed Identity.");
|
|
2218
|
+
const resource = mapScopesToResource(scopes);
|
|
2219
|
+
if (!resource) {
|
|
2220
|
+
throw new CredentialUnavailableError(`ManagedIdentityCredential: Multiple scopes are not supported. Scopes: ${JSON.stringify(scopes)}`);
|
|
2221
|
+
}
|
|
2222
|
+
return tracingClient.withSpan("ManagedIdentityCredential.getToken", options, async () => {
|
|
2223
|
+
try {
|
|
2224
|
+
const isTokenExchangeMsi = await tokenExchangeMsi.isAvailable({
|
|
2225
|
+
scopes,
|
|
2226
|
+
clientId: this.clientId,
|
|
2227
|
+
getTokenOptions: options,
|
|
2228
|
+
identityClient: this.identityClient,
|
|
2229
|
+
resourceId: this.resourceId,
|
|
2230
|
+
});
|
|
2231
|
+
// Most scenarios are handled by MSAL except for two:
|
|
2232
|
+
// AKS pod identity - MSAL does not implement the token exchange flow.
|
|
2233
|
+
// IMDS Endpoint probing - MSAL does not do any probing before trying to get a token.
|
|
2234
|
+
// As a DefaultAzureCredential optimization we probe the IMDS endpoint with a short timeout and no retries before actually trying to get a token
|
|
2235
|
+
// We will continue to implement these features in the Identity library.
|
|
2236
|
+
const identitySource = this.managedIdentityApp.getManagedIdentitySource();
|
|
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}`);
|
|
2239
|
+
if (isTokenExchangeMsi) {
|
|
2240
|
+
// In the AKS scenario we will use the existing tokenExchangeMsi indefinitely.
|
|
2241
|
+
logger$e.getToken.info("Using the token exchange managed identity.");
|
|
2242
|
+
const result = await tokenExchangeMsi.getToken({
|
|
2243
|
+
scopes,
|
|
2244
|
+
clientId: this.clientId,
|
|
2245
|
+
identityClient: this.identityClient,
|
|
2246
|
+
retryConfig: this.msiRetryConfig,
|
|
2247
|
+
resourceId: this.resourceId,
|
|
2248
|
+
});
|
|
2249
|
+
if (result === null) {
|
|
2250
|
+
throw new CredentialUnavailableError("Attempted to use the token exchange managed identity, but received a null response.");
|
|
2251
|
+
}
|
|
2252
|
+
return result;
|
|
2671
2253
|
}
|
|
2672
|
-
if (
|
|
2673
|
-
//
|
|
2674
|
-
//
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2254
|
+
else if (isImdsMsi) {
|
|
2255
|
+
// In the IMDS scenario we will probe the IMDS endpoint to ensure it's available before trying to get a token.
|
|
2256
|
+
// If the IMDS endpoint is not available and this is the source that MSAL will use, we will fail-fast with an error that tells DAC to move to the next credential.
|
|
2257
|
+
logger$e.getToken.info("Using the IMDS endpoint to probe for availability.");
|
|
2258
|
+
const isAvailable = await imdsMsi.isAvailable({
|
|
2259
|
+
scopes,
|
|
2260
|
+
clientId: this.clientId,
|
|
2261
|
+
getTokenOptions: options,
|
|
2262
|
+
identityClient: this.isAvailableIdentityClient,
|
|
2263
|
+
resourceId: this.resourceId,
|
|
2264
|
+
});
|
|
2265
|
+
if (!isAvailable) {
|
|
2266
|
+
throw new CredentialUnavailableError(`ManagedIdentityCredential: Attempted to use the IMDS endpoint, but it is not available.`);
|
|
2267
|
+
}
|
|
2682
2268
|
}
|
|
2683
|
-
//
|
|
2684
|
-
//
|
|
2685
|
-
//
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
logger$e.getToken.info(
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
}
|
|
2698
|
-
catch (err) {
|
|
2699
|
-
// CredentialUnavailable errors are expected to reach here.
|
|
2700
|
-
// We intend them to bubble up, so that DefaultAzureCredential can catch them.
|
|
2701
|
-
if (err.name === "AuthenticationRequiredError") {
|
|
2702
|
-
throw err;
|
|
2703
|
-
}
|
|
2704
|
-
// Expected errors to reach this point:
|
|
2705
|
-
// - Errors coming from a method unexpectedly breaking.
|
|
2706
|
-
// - When identityClient.sendTokenRequest throws, in which case
|
|
2707
|
-
// if the status code was 400, it means that the endpoint is working,
|
|
2708
|
-
// but no identity is available.
|
|
2709
|
-
span.setStatus({
|
|
2710
|
-
status: "error",
|
|
2711
|
-
error: err,
|
|
2712
|
-
});
|
|
2713
|
-
// If either the network is unreachable,
|
|
2714
|
-
// we can safely assume the credential is unavailable.
|
|
2715
|
-
if (err.code === "ENETUNREACH") {
|
|
2716
|
-
const error = new CredentialUnavailableError(`ManagedIdentityCredential: Unavailable. Network unreachable. Message: ${err.message}`);
|
|
2717
|
-
logger$e.getToken.info(formatError(scopes, error));
|
|
2718
|
-
throw error;
|
|
2719
|
-
}
|
|
2720
|
-
// If either the host was unreachable,
|
|
2721
|
-
// we can safely assume the credential is unavailable.
|
|
2722
|
-
if (err.code === "EHOSTUNREACH") {
|
|
2723
|
-
const error = new CredentialUnavailableError(`ManagedIdentityCredential: Unavailable. No managed identity endpoint found. Message: ${err.message}`);
|
|
2724
|
-
logger$e.getToken.info(formatError(scopes, error));
|
|
2725
|
-
throw error;
|
|
2726
|
-
}
|
|
2727
|
-
// If err.statusCode has a value of 400, it comes from sendTokenRequest,
|
|
2728
|
-
// and it means that the endpoint is working, but that no identity is available.
|
|
2729
|
-
if (err.statusCode === 400) {
|
|
2730
|
-
throw new CredentialUnavailableError(`ManagedIdentityCredential: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`);
|
|
2269
|
+
// If we got this far, it means:
|
|
2270
|
+
// - This is not a tokenExchangeMsi,
|
|
2271
|
+
// - We already probed for IMDS endpoint availability and failed-fast if it's unreachable.
|
|
2272
|
+
// We can proceed normally by calling MSAL for a token.
|
|
2273
|
+
logger$e.getToken.info("Calling into MSAL for managed identity token.");
|
|
2274
|
+
const token = await this.managedIdentityApp.acquireToken({
|
|
2275
|
+
resource,
|
|
2276
|
+
});
|
|
2277
|
+
this.ensureValidMsalToken(scopes, token, options);
|
|
2278
|
+
logger$e.getToken.info(formatSuccess(scopes));
|
|
2279
|
+
return {
|
|
2280
|
+
expiresOnTimestamp: token.expiresOn.getTime(),
|
|
2281
|
+
token: token.accessToken,
|
|
2282
|
+
};
|
|
2731
2283
|
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
throw error;
|
|
2284
|
+
catch (err) {
|
|
2285
|
+
logger$e.getToken.error(formatError(scopes, err));
|
|
2286
|
+
// AuthenticationRequiredError described as Error to enforce authentication after trying to retrieve a token silently.
|
|
2287
|
+
// TODO: why would this _ever_ happen considering we're not trying the silent request in this flow?
|
|
2288
|
+
if (err.name === "AuthenticationRequiredError") {
|
|
2289
|
+
throw err;
|
|
2739
2290
|
}
|
|
2291
|
+
if (isNetworkError(err)) {
|
|
2292
|
+
throw new CredentialUnavailableError(`ManagedIdentityCredential: Network unreachable. Message: ${err.message}`, { cause: err });
|
|
2293
|
+
}
|
|
2294
|
+
throw new CredentialUnavailableError(`ManagedIdentityCredential: Authentication failed. Message ${err.message}`, { cause: err });
|
|
2740
2295
|
}
|
|
2741
|
-
|
|
2742
|
-
// This will throw silently during any ChainedTokenCredential.
|
|
2743
|
-
if (err.statusCode === undefined) {
|
|
2744
|
-
throw new CredentialUnavailableError(`ManagedIdentityCredential: Authentication failed. Message ${err.message}`);
|
|
2745
|
-
}
|
|
2746
|
-
// Any other error should break the chain.
|
|
2747
|
-
throw new AuthenticationError(err.statusCode, {
|
|
2748
|
-
error: `ManagedIdentityCredential authentication failed.`,
|
|
2749
|
-
error_description: err.message,
|
|
2750
|
-
});
|
|
2751
|
-
}
|
|
2752
|
-
finally {
|
|
2753
|
-
// Finally is always called, both if we return and if we throw in the above try/catch.
|
|
2754
|
-
span.end();
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
/**
|
|
2758
|
-
* Handles the MSAL authentication result.
|
|
2759
|
-
* If the result has an account, we update the local account reference.
|
|
2760
|
-
* If the token received is invalid, an error will be thrown depending on what's missing.
|
|
2761
|
-
*/
|
|
2762
|
-
handleResult(scopes, result, getTokenOptions) {
|
|
2763
|
-
this.ensureValidMsalToken(scopes, result, getTokenOptions);
|
|
2764
|
-
logger$e.getToken.info(formatSuccess(scopes));
|
|
2765
|
-
return {
|
|
2766
|
-
token: result.accessToken,
|
|
2767
|
-
expiresOnTimestamp: result.expiresOn.getTime(),
|
|
2768
|
-
};
|
|
2296
|
+
});
|
|
2769
2297
|
}
|
|
2770
2298
|
/**
|
|
2771
2299
|
* Ensures the validity of the MSAL token
|
|
2772
2300
|
*/
|
|
2773
2301
|
ensureValidMsalToken(scopes, msalToken, getTokenOptions) {
|
|
2774
|
-
const
|
|
2302
|
+
const createError = (message) => {
|
|
2775
2303
|
logger$e.getToken.info(message);
|
|
2776
2304
|
return new AuthenticationRequiredError({
|
|
2777
2305
|
scopes: Array.isArray(scopes) ? scopes : [scopes],
|
|
@@ -2780,43 +2308,33 @@ class LegacyMsiProvider {
|
|
|
2780
2308
|
});
|
|
2781
2309
|
};
|
|
2782
2310
|
if (!msalToken) {
|
|
2783
|
-
throw
|
|
2311
|
+
throw createError("No response.");
|
|
2784
2312
|
}
|
|
2785
2313
|
if (!msalToken.expiresOn) {
|
|
2786
|
-
throw
|
|
2314
|
+
throw createError(`Response had no "expiresOn" property.`);
|
|
2787
2315
|
}
|
|
2788
2316
|
if (!msalToken.accessToken) {
|
|
2789
|
-
throw
|
|
2790
|
-
}
|
|
2791
|
-
}
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
};
|
|
2808
|
-
}
|
|
2809
|
-
else {
|
|
2810
|
-
logger$e.info(`SetAppTokenProvider token has "no_access_token_returned" as the saved token`);
|
|
2811
|
-
return {
|
|
2812
|
-
accessToken: "no_access_token_returned",
|
|
2813
|
-
expiresInSeconds: 0,
|
|
2814
|
-
};
|
|
2815
|
-
}
|
|
2816
|
-
});
|
|
2817
|
-
this.isAppTokenProviderInitialized = true;
|
|
2317
|
+
throw createError(`Response had no "accessToken" property.`);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
function isNetworkError(err) {
|
|
2322
|
+
// MSAL error
|
|
2323
|
+
if (err.errorCode === "network_error") {
|
|
2324
|
+
return true;
|
|
2325
|
+
}
|
|
2326
|
+
// Probe errors
|
|
2327
|
+
if (err.code === "ENETUNREACH" || err.code === "EHOSTUNREACH") {
|
|
2328
|
+
return true;
|
|
2329
|
+
}
|
|
2330
|
+
// This is a special case for Docker Desktop which responds with a 403 with a message that contains "A socket operation was attempted to an unreachable network" or "A socket operation was attempted to an unreachable host"
|
|
2331
|
+
// rather than just timing out, as expected.
|
|
2332
|
+
if (err.statusCode === 403 || err.code === 403) {
|
|
2333
|
+
if (err.message.includes("unreachable")) {
|
|
2334
|
+
return true;
|
|
2818
2335
|
}
|
|
2819
2336
|
}
|
|
2337
|
+
return false;
|
|
2820
2338
|
}
|
|
2821
2339
|
|
|
2822
2340
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -2835,7 +2353,11 @@ class ManagedIdentityCredential {
|
|
|
2835
2353
|
* @hidden
|
|
2836
2354
|
*/
|
|
2837
2355
|
constructor(clientIdOrOptions, options) {
|
|
2838
|
-
|
|
2356
|
+
// https://github.com/Azure/azure-sdk-for-js/issues/30189
|
|
2357
|
+
// If needed, you may release a hotfix to quickly rollback to the legacy implementation by changing the following line to:
|
|
2358
|
+
// this.implProvider = new LegacyMsiProvider(clientIdOrOptions, options);
|
|
2359
|
+
// Once stabilized, you can remove the legacy implementation and inline the msalMsiProvider code here as a drop-in replacement.
|
|
2360
|
+
this.implProvider = new MsalMsiProvider(clientIdOrOptions, options);
|
|
2839
2361
|
}
|
|
2840
2362
|
/**
|
|
2841
2363
|
* Authenticates with Microsoft Entra ID and returns an access token if successful.
|
|
@@ -3345,33 +2867,45 @@ class AzurePowerShellCredential {
|
|
|
3345
2867
|
commandStack.shift();
|
|
3346
2868
|
continue;
|
|
3347
2869
|
}
|
|
3348
|
-
let tenantSection = "";
|
|
3349
|
-
if (tenantId) {
|
|
3350
|
-
tenantSection = `-TenantId "${tenantId}"`;
|
|
3351
|
-
}
|
|
3352
2870
|
const results = await runCommands([
|
|
3353
2871
|
[
|
|
3354
2872
|
powerShellCommand,
|
|
3355
2873
|
"-NoProfile",
|
|
3356
2874
|
"-NonInteractive",
|
|
3357
2875
|
"-Command",
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
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
|
+
`,
|
|
3366
2905
|
],
|
|
3367
2906
|
]);
|
|
3368
|
-
const result = results[
|
|
3369
|
-
|
|
3370
|
-
return JSON.parse(result);
|
|
3371
|
-
}
|
|
3372
|
-
catch (e) {
|
|
3373
|
-
throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`);
|
|
3374
|
-
}
|
|
2907
|
+
const result = results[0];
|
|
2908
|
+
return parseJsonToken(result);
|
|
3375
2909
|
}
|
|
3376
2910
|
throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`);
|
|
3377
2911
|
}
|
|
@@ -3418,6 +2952,38 @@ class AzurePowerShellCredential {
|
|
|
3418
2952
|
});
|
|
3419
2953
|
}
|
|
3420
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
|
+
}
|
|
3421
2987
|
|
|
3422
2988
|
// Copyright (c) Microsoft Corporation.
|
|
3423
2989
|
// Licensed under the MIT license.
|
|
@@ -3550,7 +3116,8 @@ class ClientCertificateCredential {
|
|
|
3550
3116
|
});
|
|
3551
3117
|
}
|
|
3552
3118
|
async buildClientCertificate() {
|
|
3553
|
-
|
|
3119
|
+
var _a;
|
|
3120
|
+
const parts = await parseCertificate(this.certificateConfiguration, (_a = this.sendCertificateChain) !== null && _a !== void 0 ? _a : false);
|
|
3554
3121
|
let privateKey;
|
|
3555
3122
|
if (this.certificateConfiguration.certificatePassword !== undefined) {
|
|
3556
3123
|
privateKey = crypto.createPrivateKey({
|
|
@@ -3573,34 +3140,41 @@ class ClientCertificateCredential {
|
|
|
3573
3140
|
x5c: parts.x5c,
|
|
3574
3141
|
};
|
|
3575
3142
|
}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
.
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
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]);
|
|
3164
|
+
}
|
|
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
|
+
};
|
|
3604
3178
|
}
|
|
3605
3179
|
|
|
3606
3180
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -3626,8 +3200,14 @@ class ClientSecretCredential {
|
|
|
3626
3200
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
3627
3201
|
*/
|
|
3628
3202
|
constructor(tenantId, clientId, clientSecret, options = {}) {
|
|
3629
|
-
if (!tenantId
|
|
3630
|
-
throw new
|
|
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.");
|
|
3631
3211
|
}
|
|
3632
3212
|
this.clientSecret = clientSecret;
|
|
3633
3213
|
this.tenantId = tenantId;
|
|
@@ -3673,8 +3253,17 @@ class UsernamePasswordCredential {
|
|
|
3673
3253
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
3674
3254
|
*/
|
|
3675
3255
|
constructor(tenantId, clientId, username, password, options = {}) {
|
|
3676
|
-
if (!tenantId
|
|
3677
|
-
throw new
|
|
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.");
|
|
3678
3267
|
}
|
|
3679
3268
|
this.tenantId = tenantId;
|
|
3680
3269
|
this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants);
|
|
@@ -3721,6 +3310,7 @@ const AllSupportedEnvironmentVariables = [
|
|
|
3721
3310
|
"AZURE_USERNAME",
|
|
3722
3311
|
"AZURE_PASSWORD",
|
|
3723
3312
|
"AZURE_ADDITIONALLY_ALLOWED_TENANTS",
|
|
3313
|
+
"AZURE_CLIENT_SEND_CERTIFICATE_CHAIN",
|
|
3724
3314
|
];
|
|
3725
3315
|
function getAdditionallyAllowedTenants() {
|
|
3726
3316
|
var _a;
|
|
@@ -3729,6 +3319,13 @@ function getAdditionallyAllowedTenants() {
|
|
|
3729
3319
|
}
|
|
3730
3320
|
const credentialName$2 = "EnvironmentCredential";
|
|
3731
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
|
+
}
|
|
3732
3329
|
/**
|
|
3733
3330
|
* Enables authentication to Microsoft Entra ID using a client secret or certificate, or as a user
|
|
3734
3331
|
* with a username and password.
|
|
@@ -3748,6 +3345,7 @@ class EnvironmentCredential {
|
|
|
3748
3345
|
* - `AZURE_CLIENT_SECRET`: A client secret that was generated for the App Registration.
|
|
3749
3346
|
* - `AZURE_CLIENT_CERTIFICATE_PATH`: The path to a PEM certificate to use during the authentication, instead of the client secret.
|
|
3750
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.
|
|
3751
3349
|
*
|
|
3752
3350
|
* Alternatively, users can provide environment variables for username and password authentication:
|
|
3753
3351
|
* - `AZURE_USERNAME`: Username to authenticate with.
|
|
@@ -3765,7 +3363,8 @@ class EnvironmentCredential {
|
|
|
3765
3363
|
logger$6.info(`Found the following environment variables: ${assigned}`);
|
|
3766
3364
|
const tenantId = process.env.AZURE_TENANT_ID, clientId = process.env.AZURE_CLIENT_ID, clientSecret = process.env.AZURE_CLIENT_SECRET;
|
|
3767
3365
|
const additionallyAllowedTenantIds = getAdditionallyAllowedTenants();
|
|
3768
|
-
const
|
|
3366
|
+
const sendCertificateChain = getSendCertificateChain();
|
|
3367
|
+
const newOptions = Object.assign(Object.assign({}, options), { additionallyAllowedTenantIds, sendCertificateChain });
|
|
3769
3368
|
if (tenantId) {
|
|
3770
3369
|
checkTenantId(logger$6, tenantId);
|
|
3771
3370
|
}
|
|
@@ -4026,7 +3625,7 @@ class InteractiveBrowserCredential {
|
|
|
4026
3625
|
* Authenticates with Microsoft Entra ID and returns an access token if successful.
|
|
4027
3626
|
* If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
|
|
4028
3627
|
*
|
|
4029
|
-
* If the token can't be retrieved silently, this method will
|
|
3628
|
+
* If the token can't be retrieved silently, this method will always generate a challenge for the user.
|
|
4030
3629
|
*
|
|
4031
3630
|
* On Node.js, this credential has [Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636) enabled by default.
|
|
4032
3631
|
* PKCE is a security feature that mitigates authentication code interception attacks.
|
|
@@ -4112,7 +3711,7 @@ class DeviceCodeCredential {
|
|
|
4112
3711
|
* Authenticates with Microsoft Entra ID and returns an access token if successful.
|
|
4113
3712
|
* If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
|
|
4114
3713
|
*
|
|
4115
|
-
* If the token can't be retrieved silently, this method will
|
|
3714
|
+
* If the token can't be retrieved silently, this method will always generate a challenge for the user.
|
|
4116
3715
|
*
|
|
4117
3716
|
* @param scopes - The list of scopes for which the token will have access.
|
|
4118
3717
|
* @param options - The options used to configure any requests this
|
|
@@ -4146,8 +3745,17 @@ class AzurePipelinesCredential {
|
|
|
4146
3745
|
* @param options - The identity client options to use for authentication.
|
|
4147
3746
|
*/
|
|
4148
3747
|
constructor(tenantId, clientId, serviceConnectionId, systemAccessToken, options) {
|
|
4149
|
-
if (!clientId
|
|
4150
|
-
throw new CredentialUnavailableError(`${credentialName$1}: is unavailable.
|
|
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.`);
|
|
4151
3759
|
}
|
|
4152
3760
|
this.identityClient = new IdentityClient(options);
|
|
4153
3761
|
checkTenantId(logger$2, tenantId);
|
|
@@ -4200,31 +3808,46 @@ class AzurePipelinesCredential {
|
|
|
4200
3808
|
}),
|
|
4201
3809
|
});
|
|
4202
3810
|
const response = await this.identityClient.sendRequest(request);
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
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;
|
|
4207
3827
|
}
|
|
4208
|
-
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
else {
|
|
4214
|
-
let errorMessage = `${credentialName$1}: Authentication Failed. oidcToken field not detected in the response.`;
|
|
4215
|
-
if (response.status !== 200) {
|
|
4216
|
-
errorMessage += `Response = ${JSON.stringify(result)}`;
|
|
4217
|
-
}
|
|
4218
|
-
logger$2.error(errorMessage);
|
|
4219
|
-
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`;
|
|
4220
3833
|
}
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
3834
|
+
logger$2.error(errorMessage);
|
|
3835
|
+
logger$2.error(errorDescription);
|
|
3836
|
+
throw new AuthenticationError(response.status, {
|
|
3837
|
+
error: errorMessage,
|
|
3838
|
+
error_description: errorDescription,
|
|
3839
|
+
});
|
|
4226
3840
|
}
|
|
4227
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
|
+
});
|
|
3850
|
+
}
|
|
4228
3851
|
}
|
|
4229
3852
|
|
|
4230
3853
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -4294,11 +3917,17 @@ class OnBehalfOfCredential {
|
|
|
4294
3917
|
const { certificatePath, sendCertificateChain } = options;
|
|
4295
3918
|
const { getAssertion } = options;
|
|
4296
3919
|
const { tenantId, clientId, userAssertionToken, additionallyAllowedTenants: additionallyAllowedTenantIds, } = options;
|
|
4297
|
-
if (!tenantId
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
throw new
|
|
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.`);
|
|
4302
3931
|
}
|
|
4303
3932
|
this.certificatePath = certificatePath;
|
|
4304
3933
|
this.clientSecret = clientSecret;
|
|
@@ -4467,7 +4096,7 @@ exports.WorkloadIdentityCredential = WorkloadIdentityCredential;
|
|
|
4467
4096
|
exports.deserializeAuthenticationRecord = deserializeAuthenticationRecord;
|
|
4468
4097
|
exports.getBearerTokenProvider = getBearerTokenProvider;
|
|
4469
4098
|
exports.getDefaultAzureCredential = getDefaultAzureCredential;
|
|
4470
|
-
exports.logger = logger$
|
|
4099
|
+
exports.logger = logger$l;
|
|
4471
4100
|
exports.serializeAuthenticationRecord = serializeAuthenticationRecord;
|
|
4472
4101
|
exports.useIdentityPlugin = useIdentityPlugin;
|
|
4473
4102
|
//# sourceMappingURL=index.js.map
|