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