@azure/identity 2.0.0-beta.5 → 2.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @azure/identity might be problematic. Click here for more details.
- package/CHANGELOG.md +49 -10
- package/README.md +26 -21
- package/dist/index.js +461 -211
- package/dist/index.js.map +1 -1
- package/dist-esm/src/client/errors.js +1 -1
- package/dist-esm/src/client/errors.js.map +1 -1
- package/dist-esm/src/client/identityClient.js +2 -6
- package/dist-esm/src/client/identityClient.js.map +1 -1
- package/dist-esm/src/credentials/applicationCredential.js +0 -3
- package/dist-esm/src/credentials/applicationCredential.js.map +1 -1
- package/dist-esm/src/credentials/authorizationCodeCredential.js +12 -76
- package/dist-esm/src/credentials/authorizationCodeCredential.js.map +1 -1
- package/dist-esm/src/credentials/chainedTokenCredential.js +4 -3
- package/dist-esm/src/credentials/chainedTokenCredential.js.map +1 -1
- package/dist-esm/src/credentials/clientCertificateCredential.js +3 -0
- package/dist-esm/src/credentials/clientCertificateCredential.js.map +1 -1
- package/dist-esm/src/credentials/clientSecretCredential.browser.js +1 -4
- package/dist-esm/src/credentials/clientSecretCredential.browser.js.map +1 -1
- package/dist-esm/src/credentials/clientSecretCredential.js +3 -0
- package/dist-esm/src/credentials/clientSecretCredential.js.map +1 -1
- package/dist-esm/src/credentials/credentialPersistenceOptions.js.map +1 -1
- package/dist-esm/src/credentials/defaultAzureCredential.js +5 -8
- package/dist-esm/src/credentials/defaultAzureCredential.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js +21 -10
- package/dist-esm/src/credentials/managedIdentityCredential/appServiceMsi2017.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/arcMsi.js +23 -12
- package/dist-esm/src/credentials/managedIdentityCredential/arcMsi.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/cloudShellMsi.js +22 -11
- package/dist-esm/src/credentials/managedIdentityCredential/cloudShellMsi.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js +19 -7
- package/dist-esm/src/credentials/managedIdentityCredential/fabricMsi.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js +29 -20
- package/dist-esm/src/credentials/managedIdentityCredential/imdsMsi.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/index.js +13 -10
- package/dist-esm/src/credentials/managedIdentityCredential/index.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/models.js.map +1 -1
- package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js +82 -0
- package/dist-esm/src/credentials/managedIdentityCredential/tokenExchangeMsi.js.map +1 -0
- package/dist-esm/src/credentials/managedIdentityCredential/utils.js +10 -5
- package/dist-esm/src/credentials/managedIdentityCredential/utils.js.map +1 -1
- package/dist-esm/src/credentials/onBehalfOfCredential.browser.js +17 -0
- package/dist-esm/src/credentials/onBehalfOfCredential.browser.js.map +1 -0
- package/dist-esm/src/credentials/onBehalfOfCredential.js +62 -0
- package/dist-esm/src/credentials/onBehalfOfCredential.js.map +1 -0
- package/dist-esm/src/credentials/{visualStudioCodeCredentialExtension.js → onBehalfOfCredentialOptions.js} +1 -1
- package/dist-esm/src/credentials/onBehalfOfCredentialOptions.js.map +1 -0
- package/dist-esm/src/credentials/usernamePasswordCredential.browser.js +10 -13
- package/dist-esm/src/credentials/usernamePasswordCredential.browser.js.map +1 -1
- package/dist-esm/src/credentials/usernamePasswordCredential.js +3 -0
- package/dist-esm/src/credentials/usernamePasswordCredential.js.map +1 -1
- package/dist-esm/src/credentials/visualStudioCodeCredential.browser.js +1 -1
- package/dist-esm/src/credentials/visualStudioCodeCredential.browser.js.map +1 -1
- package/dist-esm/src/credentials/visualStudioCodeCredential.js +11 -1
- package/dist-esm/src/credentials/visualStudioCodeCredential.js.map +1 -1
- package/dist-esm/src/credentials/visualStudioCodeCredentialPlugin.js +4 -0
- package/dist-esm/src/credentials/visualStudioCodeCredentialPlugin.js.map +1 -0
- package/dist-esm/src/index.js +2 -1
- package/dist-esm/src/index.js.map +1 -1
- package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js +41 -0
- package/dist-esm/src/msal/nodeFlows/msalAuthorizationCode.js.map +1 -0
- package/dist-esm/src/msal/nodeFlows/msalClientCertificate.js +48 -29
- 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/msalOnBehalfOf.js +56 -0
- package/dist-esm/src/msal/nodeFlows/msalOnBehalfOf.js.map +1 -0
- package/dist-esm/src/msal/nodeFlows/nodeCommon.js +6 -1
- package/dist-esm/src/msal/nodeFlows/nodeCommon.js.map +1 -1
- package/dist-esm/src/msal/nodeFlows/tokenCachePersistenceOptions.js.map +1 -1
- package/dist-esm/src/plugins/consumer.browser.js +7 -0
- package/dist-esm/src/plugins/consumer.browser.js.map +1 -0
- package/dist-esm/src/{extensions → plugins}/consumer.js +12 -12
- package/dist-esm/src/plugins/consumer.js.map +1 -0
- package/dist-esm/src/{extensions → plugins}/provider.js +0 -0
- package/dist-esm/src/plugins/provider.js.map +1 -0
- package/package.json +10 -12
- package/types/identity.d.ts +126 -41
- package/dist-esm/src/credentials/visualStudioCodeCredentialExtension.js.map +0 -1
- package/dist-esm/src/extensions/consumer.browser.js +0 -7
- package/dist-esm/src/extensions/consumer.browser.js.map +0 -1
- package/dist-esm/src/extensions/consumer.js.map +0 -1
- package/dist-esm/src/extensions/provider.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var coreTracing = require('@azure/core-tracing');
|
|
|
10
10
|
var coreUtil = require('@azure/core-util');
|
|
11
11
|
var coreRestPipeline = require('@azure/core-rest-pipeline');
|
|
12
12
|
var abortController = require('@azure/abort-controller');
|
|
13
|
-
var logger$
|
|
13
|
+
var logger$j = require('@azure/logger');
|
|
14
14
|
var msalCommon = require('@azure/msal-common');
|
|
15
15
|
var uuid = require('uuid');
|
|
16
16
|
var fs = require('fs');
|
|
@@ -19,7 +19,7 @@ var os = _interopDefault(require('os'));
|
|
|
19
19
|
var path = _interopDefault(require('path'));
|
|
20
20
|
var child_process = require('child_process');
|
|
21
21
|
var crypto = require('crypto');
|
|
22
|
-
var
|
|
22
|
+
var util = require('util');
|
|
23
23
|
var http = _interopDefault(require('http'));
|
|
24
24
|
var open = _interopDefault(require('open'));
|
|
25
25
|
var stoppable = _interopDefault(require('stoppable'));
|
|
@@ -149,7 +149,7 @@ const AggregateAuthenticationErrorName = "AggregateAuthenticationError";
|
|
|
149
149
|
class AggregateAuthenticationError extends Error {
|
|
150
150
|
constructor(errors, errorMessage) {
|
|
151
151
|
const errorDetail = errors.join("\n");
|
|
152
|
-
super(`${errorMessage}\n
|
|
152
|
+
super(`${errorMessage}\n${errorDetail}`);
|
|
153
153
|
this.errors = errors;
|
|
154
154
|
// Ensure that this type reports the correct name
|
|
155
155
|
this.name = AggregateAuthenticationErrorName;
|
|
@@ -224,7 +224,7 @@ async function trace(operationName, options, fn, createSpanFn = createSpan) {
|
|
|
224
224
|
/**
|
|
225
225
|
* The AzureLogger used for all clients within the identity package
|
|
226
226
|
*/
|
|
227
|
-
const logger = logger$
|
|
227
|
+
const logger = logger$j.createClientLogger("identity");
|
|
228
228
|
/**
|
|
229
229
|
* Separates a list of environment variable names into a plain object with two arrays: an array of missing environment variables and another array with assigned environment variables.
|
|
230
230
|
* @param supportedEnvVars - List of environment variable names
|
|
@@ -315,7 +315,7 @@ function getIdentityClientAuthorityHost(options) {
|
|
|
315
315
|
class IdentityClient extends coreClient.ServiceClient {
|
|
316
316
|
constructor(options) {
|
|
317
317
|
var _a;
|
|
318
|
-
const packageDetails = `azsdk-js-identity/2.0.0-beta.
|
|
318
|
+
const packageDetails = `azsdk-js-identity/2.0.0-beta.6`;
|
|
319
319
|
const userAgentPrefix = ((_a = options === null || options === void 0 ? void 0 : options.userAgentOptions) === null || _a === void 0 ? void 0 : _a.userAgentPrefix)
|
|
320
320
|
? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}`
|
|
321
321
|
: `${packageDetails}`;
|
|
@@ -359,7 +359,6 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
async refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, expiresOnParser, options) {
|
|
362
|
-
var _a, _b;
|
|
363
362
|
if (refreshToken === undefined) {
|
|
364
363
|
return null;
|
|
365
364
|
}
|
|
@@ -386,10 +385,7 @@ class IdentityClient extends coreClient.ServiceClient {
|
|
|
386
385
|
Accept: "application/json",
|
|
387
386
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
388
387
|
}),
|
|
389
|
-
tracingOptions:
|
|
390
|
-
spanOptions: (_a = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _a === void 0 ? void 0 : _a.spanOptions,
|
|
391
|
-
tracingContext: (_b = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _b === void 0 ? void 0 : _b.tracingContext
|
|
392
|
-
}
|
|
388
|
+
tracingOptions: updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions
|
|
393
389
|
});
|
|
394
390
|
const response = await this.sendTokenRequest(request, expiresOnParser);
|
|
395
391
|
logger.info(`IdentityClient: refreshed token for client ID: ${clientId}`);
|
|
@@ -913,7 +909,12 @@ class MsalNode extends MsalBaseUtilities {
|
|
|
913
909
|
this.createCachePlugin = () => persistenceProvider(options.tokenCachePersistenceOptions);
|
|
914
910
|
}
|
|
915
911
|
else if ((_b = options.tokenCachePersistenceOptions) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
916
|
-
throw new Error(
|
|
912
|
+
throw new Error([
|
|
913
|
+
"Persistent token caching was requested, but no persistence provider was configured.",
|
|
914
|
+
"You must install the identity-cache-persistence plugin package (`npm install --save @azure/identity-cache-persistence`)",
|
|
915
|
+
"and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling",
|
|
916
|
+
"`useIdentityPlugin(cachePersistencePlugin)` before using `tokenCachePersistenceOptions`."
|
|
917
|
+
].join(" "));
|
|
917
918
|
}
|
|
918
919
|
this.azureRegion = (_c = options.regionalAuthority) !== null && _c !== void 0 ? _c : process.env.AZURE_REGIONAL_AUTHORITY_NAME;
|
|
919
920
|
if (this.azureRegion === exports.RegionalAuthority.AutoDiscoverRegion) {
|
|
@@ -1145,6 +1146,11 @@ class VisualStudioCodeCredential {
|
|
|
1145
1146
|
/**
|
|
1146
1147
|
* Creates an instance of VisualStudioCodeCredential to use for automatically authenticating via VSCode.
|
|
1147
1148
|
*
|
|
1149
|
+
* **Note**: `VisualStudioCodeCredential` is provided by a plugin package:
|
|
1150
|
+
* `@azure/identity-vscode`. If this package is not installed and registered
|
|
1151
|
+
* using the plugin API (`useIdentityPlugin`), then authentication using
|
|
1152
|
+
* `VisualStudioCodeCredential` will not be available.
|
|
1153
|
+
*
|
|
1148
1154
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
1149
1155
|
*/
|
|
1150
1156
|
constructor(options) {
|
|
@@ -1198,7 +1204,12 @@ class VisualStudioCodeCredential {
|
|
|
1198
1204
|
const tenantId = processMultiTenantRequest(this.tenantId, this.allowMultiTenantAuthentication, options) ||
|
|
1199
1205
|
this.tenantId;
|
|
1200
1206
|
if (findCredentials === undefined) {
|
|
1201
|
-
throw new CredentialUnavailableError(
|
|
1207
|
+
throw new CredentialUnavailableError([
|
|
1208
|
+
"No implementation of `VisualStudioCodeCredential` is available.",
|
|
1209
|
+
"You must install the identity-vscode plugin package (`npm install --save-dev @azure/identity-vscode`)",
|
|
1210
|
+
"and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling",
|
|
1211
|
+
"`useIdentityPlugin(vsCodePlugin)` before creating a `VisualStudioCodeCredential`."
|
|
1212
|
+
].join(" "));
|
|
1202
1213
|
}
|
|
1203
1214
|
let scopeString = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
1204
1215
|
// Check to make sure the scope we get back is a valid scope
|
|
@@ -1243,17 +1254,17 @@ class VisualStudioCodeCredential {
|
|
|
1243
1254
|
|
|
1244
1255
|
// Copyright (c) Microsoft Corporation.
|
|
1245
1256
|
/**
|
|
1246
|
-
* The context passed to an Identity
|
|
1247
|
-
*
|
|
1257
|
+
* The context passed to an Identity plugin. This contains objects that
|
|
1258
|
+
* plugins can use to set backend implementations.
|
|
1248
1259
|
* @internal
|
|
1249
1260
|
*/
|
|
1250
|
-
const
|
|
1261
|
+
const pluginContext = {
|
|
1251
1262
|
cachePluginControl: msalNodeFlowCacheControl,
|
|
1252
1263
|
vsCodeCredentialControl: vsCodeCredentialControl
|
|
1253
1264
|
};
|
|
1254
1265
|
/**
|
|
1255
|
-
* Extend Azure Identity with additional functionality. Pass
|
|
1256
|
-
*
|
|
1266
|
+
* Extend Azure Identity with additional functionality. Pass a plugin from
|
|
1267
|
+
* a plugin package, such as:
|
|
1257
1268
|
*
|
|
1258
1269
|
* - `@azure/identity-cache-persistence`: provides persistent token caching
|
|
1259
1270
|
* - `@azure/identity-vscode`: provides the dependencies of
|
|
@@ -1262,12 +1273,12 @@ const extensionContext = {
|
|
|
1262
1273
|
* Example:
|
|
1263
1274
|
*
|
|
1264
1275
|
* ```javascript
|
|
1265
|
-
* import {
|
|
1276
|
+
* import { cachePersistencePlugin } from "@azure/identity-cache-persistence";
|
|
1266
1277
|
*
|
|
1267
|
-
* import {
|
|
1268
|
-
*
|
|
1278
|
+
* import { useIdentityPlugin, DefaultAzureCredential } from "@azure/identity";
|
|
1279
|
+
* useIdentityPlugin(cachePersistencePlugin);
|
|
1269
1280
|
*
|
|
1270
|
-
* // The
|
|
1281
|
+
* // The plugin has the capability to extend `DefaultAzureCredential` and to
|
|
1271
1282
|
* // add middleware to the underlying credentials, such as persistence.
|
|
1272
1283
|
* const credential = new DefaultAzureCredential({
|
|
1273
1284
|
* tokenCachePersistenceOptions: {
|
|
@@ -1276,10 +1287,10 @@ const extensionContext = {
|
|
|
1276
1287
|
* });
|
|
1277
1288
|
* ```
|
|
1278
1289
|
*
|
|
1279
|
-
* @param
|
|
1290
|
+
* @param plugin - the plugin to register
|
|
1280
1291
|
*/
|
|
1281
|
-
function
|
|
1282
|
-
|
|
1292
|
+
function useIdentityPlugin(plugin) {
|
|
1293
|
+
plugin(pluginContext);
|
|
1283
1294
|
}
|
|
1284
1295
|
|
|
1285
1296
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -1327,12 +1338,13 @@ class ChainedTokenCredential {
|
|
|
1327
1338
|
*/
|
|
1328
1339
|
async getToken(scopes, options) {
|
|
1329
1340
|
let token = null;
|
|
1341
|
+
let successfulCredentialName = "";
|
|
1330
1342
|
const errors = [];
|
|
1331
1343
|
const { span, updatedOptions } = createSpan("ChainedTokenCredential-getToken", options);
|
|
1332
1344
|
for (let i = 0; i < this._sources.length && token === null; i++) {
|
|
1333
1345
|
try {
|
|
1334
1346
|
token = await this._sources[i].getToken(scopes, updatedOptions);
|
|
1335
|
-
|
|
1347
|
+
successfulCredentialName = this._sources[i].constructor.name;
|
|
1336
1348
|
}
|
|
1337
1349
|
catch (err) {
|
|
1338
1350
|
if (err.name === "CredentialUnavailableError" ||
|
|
@@ -1346,7 +1358,7 @@ class ChainedTokenCredential {
|
|
|
1346
1358
|
}
|
|
1347
1359
|
}
|
|
1348
1360
|
if (!token && errors.length > 0) {
|
|
1349
|
-
const err = new AggregateAuthenticationError(errors);
|
|
1361
|
+
const err = new AggregateAuthenticationError(errors, "ChainedTokenCredential authentication failed.");
|
|
1350
1362
|
span.setStatus({
|
|
1351
1363
|
code: coreTracing.SpanStatusCode.ERROR,
|
|
1352
1364
|
message: err.message
|
|
@@ -1355,7 +1367,7 @@ class ChainedTokenCredential {
|
|
|
1355
1367
|
throw err;
|
|
1356
1368
|
}
|
|
1357
1369
|
span.end();
|
|
1358
|
-
logger$2.getToken.info(`Result for ${
|
|
1370
|
+
logger$2.getToken.info(`Result for ${successfulCredentialName}: ${formatSuccess(scopes)}`);
|
|
1359
1371
|
if (token === null) {
|
|
1360
1372
|
throw new CredentialUnavailableError("Failed to retrieve a valid token");
|
|
1361
1373
|
}
|
|
@@ -1759,6 +1771,9 @@ class ClientSecretCredential {
|
|
|
1759
1771
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
1760
1772
|
*/
|
|
1761
1773
|
constructor(tenantId, clientId, clientSecret, options = {}) {
|
|
1774
|
+
if (!tenantId || !clientId || !clientSecret) {
|
|
1775
|
+
throw new Error("ClientSecretCredential: tenantId, clientId, and clientSecret are required parameters.");
|
|
1776
|
+
}
|
|
1762
1777
|
this.msalFlow = new MsalClientSecret(Object.assign(Object.assign({}, options), { logger: logger$5,
|
|
1763
1778
|
clientId,
|
|
1764
1779
|
tenantId,
|
|
@@ -1781,6 +1796,40 @@ class ClientSecretCredential {
|
|
|
1781
1796
|
}
|
|
1782
1797
|
|
|
1783
1798
|
// Copyright (c) Microsoft Corporation.
|
|
1799
|
+
const readFileAsync = util.promisify(fs.readFile);
|
|
1800
|
+
/**
|
|
1801
|
+
* Tries to asynchronously load a certificate from the given path.
|
|
1802
|
+
*
|
|
1803
|
+
* @param certificatePath - Path to the certificate.
|
|
1804
|
+
* @param sendCertificateChain - Option to include x5c header for SubjectName and Issuer name authorization.
|
|
1805
|
+
* @returns - The certificate parts, or `undefined` if the certificate could not be loaded.
|
|
1806
|
+
* @internal
|
|
1807
|
+
*/
|
|
1808
|
+
async function parseCertificate(certificatePath, sendCertificateChain) {
|
|
1809
|
+
const certificateParts = {};
|
|
1810
|
+
certificateParts.certificateContents = await readFileAsync(certificatePath, "utf8");
|
|
1811
|
+
if (sendCertificateChain) {
|
|
1812
|
+
certificateParts.x5c = certificateParts.certificateContents;
|
|
1813
|
+
}
|
|
1814
|
+
const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g;
|
|
1815
|
+
const publicKeys = [];
|
|
1816
|
+
// Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c
|
|
1817
|
+
let match;
|
|
1818
|
+
do {
|
|
1819
|
+
match = certificatePattern.exec(certificateParts.certificateContents);
|
|
1820
|
+
if (match) {
|
|
1821
|
+
publicKeys.push(match[3]);
|
|
1822
|
+
}
|
|
1823
|
+
} while (match);
|
|
1824
|
+
if (publicKeys.length === 0) {
|
|
1825
|
+
throw new Error("The file at the specified path does not contain a PEM-encoded certificate.");
|
|
1826
|
+
}
|
|
1827
|
+
certificateParts.thumbprint = crypto.createHash("sha1")
|
|
1828
|
+
.update(Buffer.from(publicKeys[0], "base64"))
|
|
1829
|
+
.digest("hex")
|
|
1830
|
+
.toUpperCase();
|
|
1831
|
+
return certificateParts;
|
|
1832
|
+
}
|
|
1784
1833
|
/**
|
|
1785
1834
|
* MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`.
|
|
1786
1835
|
* @internal
|
|
@@ -1789,40 +1838,24 @@ class MsalClientCertificate extends MsalNode {
|
|
|
1789
1838
|
constructor(options) {
|
|
1790
1839
|
super(options);
|
|
1791
1840
|
this.requiresConfidential = true;
|
|
1841
|
+
this.certificatePath = options.certificatePath;
|
|
1792
1842
|
this.sendCertificateChain = options.sendCertificateChain;
|
|
1793
|
-
const parts = this.parseCertificate(options.certificatePath);
|
|
1794
|
-
this.msalConfig.auth.clientCertificate = {
|
|
1795
|
-
thumbprint: parts.thumbprint,
|
|
1796
|
-
privateKey: parts.certificateContents,
|
|
1797
|
-
x5c: parts.x5c
|
|
1798
|
-
};
|
|
1799
1843
|
}
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
match = certificatePattern.exec(certificateParts.certificateContents);
|
|
1812
|
-
if (match) {
|
|
1813
|
-
publicKeys.push(match[3]);
|
|
1814
|
-
}
|
|
1815
|
-
} while (match);
|
|
1816
|
-
if (publicKeys.length === 0) {
|
|
1817
|
-
const error = new Error("The file at the specified path does not contain a PEM-encoded certificate.");
|
|
1844
|
+
// Changing the MSAL configuration asynchronously
|
|
1845
|
+
async init(options) {
|
|
1846
|
+
try {
|
|
1847
|
+
const parts = await parseCertificate(this.certificatePath, this.sendCertificateChain);
|
|
1848
|
+
this.msalConfig.auth.clientCertificate = {
|
|
1849
|
+
thumbprint: parts.thumbprint,
|
|
1850
|
+
privateKey: parts.certificateContents,
|
|
1851
|
+
x5c: parts.x5c
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
catch (error) {
|
|
1818
1855
|
this.logger.info(formatError("", error));
|
|
1819
1856
|
throw error;
|
|
1820
1857
|
}
|
|
1821
|
-
|
|
1822
|
-
.update(Buffer.from(publicKeys[0], "base64"))
|
|
1823
|
-
.digest("hex")
|
|
1824
|
-
.toUpperCase();
|
|
1825
|
-
return certificateParts;
|
|
1858
|
+
return super.init(options);
|
|
1826
1859
|
}
|
|
1827
1860
|
async doGetToken(scopes, options = {}) {
|
|
1828
1861
|
try {
|
|
@@ -1864,6 +1897,9 @@ class ClientCertificateCredential {
|
|
|
1864
1897
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
1865
1898
|
*/
|
|
1866
1899
|
constructor(tenantId, clientId, certificatePath, options = {}) {
|
|
1900
|
+
if (!tenantId || !clientId || !certificatePath) {
|
|
1901
|
+
throw new Error("ClientCertificateCredential: tenantId, clientId, and certificatePath are required parameters.");
|
|
1902
|
+
}
|
|
1867
1903
|
this.msalFlow = new MsalClientCertificate(Object.assign(Object.assign({}, options), { certificatePath,
|
|
1868
1904
|
logger: logger$6,
|
|
1869
1905
|
clientId,
|
|
@@ -1937,6 +1973,9 @@ class UsernamePasswordCredential {
|
|
|
1937
1973
|
* @param options - Options for configuring the client which makes the authentication request.
|
|
1938
1974
|
*/
|
|
1939
1975
|
constructor(tenantId, clientId, username, password, options = {}) {
|
|
1976
|
+
if (!tenantId || !clientId || !username || !password) {
|
|
1977
|
+
throw new Error("UsernamePasswordCredential: tenantId, clientId, username and password are required parameters.");
|
|
1978
|
+
}
|
|
1940
1979
|
this.msalFlow = new MsalUsernamePassword(Object.assign(Object.assign({}, options), { logger: logger$7,
|
|
1941
1980
|
clientId,
|
|
1942
1981
|
tenantId,
|
|
@@ -2088,11 +2127,19 @@ const imdsApiVersion = "2018-02-01";
|
|
|
2088
2127
|
const azureArcAPIVersion = "2019-11-01";
|
|
2089
2128
|
|
|
2090
2129
|
// Copyright (c) Microsoft Corporation.
|
|
2130
|
+
/**
|
|
2131
|
+
* Most MSIs send requests to the IMDS endpoint, or a similar endpoint. These are GET requests that require sending a `resource` parameter on the query.
|
|
2132
|
+
* This resource can be derived from the scopes received through the getToken call, as long as only one scope is received.
|
|
2133
|
+
* Multiple scopes assume that the resulting token will have access to multiple resources, which won't be the case.
|
|
2134
|
+
*
|
|
2135
|
+
* For that reason, when we encounter multiple scopes, we return undefined.
|
|
2136
|
+
* It's up to the individual MSI implementations to throw the errors (which helps us provide less generic errors).
|
|
2137
|
+
*/
|
|
2091
2138
|
function mapScopesToResource(scopes) {
|
|
2092
2139
|
let scope = "";
|
|
2093
2140
|
if (Array.isArray(scopes)) {
|
|
2094
2141
|
if (scopes.length !== 1) {
|
|
2095
|
-
|
|
2142
|
+
return;
|
|
2096
2143
|
}
|
|
2097
2144
|
scope = scopes[0];
|
|
2098
2145
|
}
|
|
@@ -2105,22 +2152,24 @@ function mapScopesToResource(scopes) {
|
|
|
2105
2152
|
return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix));
|
|
2106
2153
|
}
|
|
2107
2154
|
async function msiGenericGetToken(identityClient, requestOptions, expiresInParser, getTokenOptions = {}) {
|
|
2108
|
-
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal,
|
|
2109
|
-
spanOptions: getTokenOptions.tracingOptions && getTokenOptions.tracingOptions.spanOptions,
|
|
2110
|
-
tracingContext: getTokenOptions.tracingOptions && getTokenOptions.tracingOptions.tracingContext
|
|
2111
|
-
} }, requestOptions), { allowInsecureConnection: true }));
|
|
2155
|
+
const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, requestOptions), { allowInsecureConnection: true }));
|
|
2112
2156
|
const tokenResponse = await identityClient.sendTokenRequest(request, expiresInParser);
|
|
2113
2157
|
return (tokenResponse && tokenResponse.accessToken) || null;
|
|
2114
2158
|
}
|
|
2115
2159
|
|
|
2116
2160
|
// Copyright (c) Microsoft Corporation.
|
|
2117
|
-
const
|
|
2161
|
+
const msiName = "ManagedIdentityCredential - AppServiceMSI 2017";
|
|
2162
|
+
const logger$9 = credentialLogger(msiName);
|
|
2118
2163
|
function expiresInParser(requestBody) {
|
|
2119
2164
|
// Parse a date format like "06/20/2019 02:57:58 +00:00" and
|
|
2120
2165
|
// convert it into a JavaScript-formatted date
|
|
2121
2166
|
return Date.parse(requestBody.expires_on);
|
|
2122
2167
|
}
|
|
2123
|
-
function prepareRequestOptions(
|
|
2168
|
+
function prepareRequestOptions(scopes, clientId) {
|
|
2169
|
+
const resource = mapScopesToResource(scopes);
|
|
2170
|
+
if (!resource) {
|
|
2171
|
+
throw new Error(`${msiName}: Multiple scopes are not supported.`);
|
|
2172
|
+
}
|
|
2124
2173
|
const queryParameters = {
|
|
2125
2174
|
resource,
|
|
2126
2175
|
"api-version": "2017-09-01"
|
|
@@ -2131,10 +2180,10 @@ function prepareRequestOptions(resource, clientId) {
|
|
|
2131
2180
|
const query = new URLSearchParams(queryParameters);
|
|
2132
2181
|
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
2133
2182
|
if (!process.env.MSI_ENDPOINT) {
|
|
2134
|
-
throw new Error(
|
|
2183
|
+
throw new Error(`${msiName}: Missing environment variable: MSI_ENDPOINT`);
|
|
2135
2184
|
}
|
|
2136
2185
|
if (!process.env.MSI_SECRET) {
|
|
2137
|
-
throw new Error(
|
|
2186
|
+
throw new Error(`${msiName}: Missing environment variable: MSI_SECRET`);
|
|
2138
2187
|
}
|
|
2139
2188
|
return {
|
|
2140
2189
|
url: `${process.env.MSI_ENDPOINT}?${query.toString()}`,
|
|
@@ -2146,25 +2195,36 @@ function prepareRequestOptions(resource, clientId) {
|
|
|
2146
2195
|
};
|
|
2147
2196
|
}
|
|
2148
2197
|
const appServiceMsi2017 = {
|
|
2149
|
-
async isAvailable() {
|
|
2198
|
+
async isAvailable(scopes) {
|
|
2199
|
+
const resource = mapScopesToResource(scopes);
|
|
2200
|
+
if (!resource) {
|
|
2201
|
+
logger$9.info(`${msiName}: Unavailable. Multiple scopes are not supported.`);
|
|
2202
|
+
return false;
|
|
2203
|
+
}
|
|
2150
2204
|
const env = process.env;
|
|
2151
2205
|
const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET);
|
|
2152
2206
|
if (!result) {
|
|
2153
|
-
logger$9.info(
|
|
2207
|
+
logger$9.info(`${msiName}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`);
|
|
2154
2208
|
}
|
|
2155
2209
|
return result;
|
|
2156
2210
|
},
|
|
2157
|
-
async getToken(
|
|
2158
|
-
|
|
2159
|
-
|
|
2211
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2212
|
+
const { identityClient, scopes, clientId } = configuration;
|
|
2213
|
+
logger$9.info(`${msiName}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`);
|
|
2214
|
+
return msiGenericGetToken(identityClient, prepareRequestOptions(scopes, clientId), expiresInParser, getTokenOptions);
|
|
2160
2215
|
}
|
|
2161
2216
|
};
|
|
2162
2217
|
|
|
2163
2218
|
// Copyright (c) Microsoft Corporation.
|
|
2164
|
-
const
|
|
2219
|
+
const msiName$1 = "ManagedIdentityCredential - CloudShellMSI";
|
|
2220
|
+
const logger$a = credentialLogger(msiName$1);
|
|
2165
2221
|
// Cloud Shell MSI doesn't have a special expiresIn parser.
|
|
2166
2222
|
const expiresInParser$1 = undefined;
|
|
2167
|
-
function prepareRequestOptions$1(
|
|
2223
|
+
function prepareRequestOptions$1(scopes, clientId) {
|
|
2224
|
+
const resource = mapScopesToResource(scopes);
|
|
2225
|
+
if (!resource) {
|
|
2226
|
+
throw new Error(`${msiName$1}: Multiple scopes are not supported.`);
|
|
2227
|
+
}
|
|
2168
2228
|
const body = {
|
|
2169
2229
|
resource
|
|
2170
2230
|
};
|
|
@@ -2173,12 +2233,13 @@ function prepareRequestOptions$1(resource, clientId) {
|
|
|
2173
2233
|
}
|
|
2174
2234
|
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
2175
2235
|
if (!process.env.MSI_ENDPOINT) {
|
|
2176
|
-
throw new Error(
|
|
2236
|
+
throw new Error(`${msiName$1}: Missing environment variable: MSI_ENDPOINT`);
|
|
2177
2237
|
}
|
|
2238
|
+
const params = new URLSearchParams(body);
|
|
2178
2239
|
return {
|
|
2179
2240
|
url: process.env.MSI_ENDPOINT,
|
|
2180
2241
|
method: "POST",
|
|
2181
|
-
body:
|
|
2242
|
+
body: params.toString(),
|
|
2182
2243
|
headers: coreRestPipeline.createHttpHeaders({
|
|
2183
2244
|
Accept: "application/json",
|
|
2184
2245
|
Metadata: "true",
|
|
@@ -2187,37 +2248,48 @@ function prepareRequestOptions$1(resource, clientId) {
|
|
|
2187
2248
|
};
|
|
2188
2249
|
}
|
|
2189
2250
|
const cloudShellMsi = {
|
|
2190
|
-
async isAvailable() {
|
|
2251
|
+
async isAvailable(scopes) {
|
|
2252
|
+
const resource = mapScopesToResource(scopes);
|
|
2253
|
+
if (!resource) {
|
|
2254
|
+
logger$a.info(`${msiName$1}: Unavailable. Multiple scopes are not supported.`);
|
|
2255
|
+
return false;
|
|
2256
|
+
}
|
|
2191
2257
|
const result = Boolean(process.env.MSI_ENDPOINT);
|
|
2192
2258
|
if (!result) {
|
|
2193
|
-
logger$a.info(
|
|
2259
|
+
logger$a.info(`${msiName$1}: Unavailable. The environment variable MSI_ENDPOINT is needed.`);
|
|
2194
2260
|
}
|
|
2195
2261
|
return result;
|
|
2196
2262
|
},
|
|
2197
|
-
async getToken(
|
|
2198
|
-
|
|
2199
|
-
|
|
2263
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2264
|
+
const { identityClient, scopes, clientId } = configuration;
|
|
2265
|
+
logger$a.info(`${msiName$1}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`);
|
|
2266
|
+
return msiGenericGetToken(identityClient, prepareRequestOptions$1(scopes, clientId), expiresInParser$1, getTokenOptions);
|
|
2200
2267
|
}
|
|
2201
2268
|
};
|
|
2202
2269
|
|
|
2203
2270
|
// Copyright (c) Microsoft Corporation.
|
|
2204
|
-
const
|
|
2271
|
+
const msiName$2 = "ManagedIdentityCredential - IMDS";
|
|
2272
|
+
const logger$b = credentialLogger(msiName$2);
|
|
2205
2273
|
function expiresInParser$2(requestBody) {
|
|
2206
2274
|
if (requestBody.expires_on) {
|
|
2207
2275
|
// Use the expires_on timestamp if it's available
|
|
2208
2276
|
const expires = +requestBody.expires_on * 1000;
|
|
2209
|
-
logger$b.info(
|
|
2277
|
+
logger$b.info(`${msiName$2}: IMDS using expires_on: ${expires} (original value: ${requestBody.expires_on})`);
|
|
2210
2278
|
return expires;
|
|
2211
2279
|
}
|
|
2212
2280
|
else {
|
|
2213
2281
|
// If these aren't possible, use expires_in and calculate a timestamp
|
|
2214
2282
|
const expires = Date.now() + requestBody.expires_in * 1000;
|
|
2215
|
-
logger$b.info(
|
|
2283
|
+
logger$b.info(`${msiName$2}: IMDS using expires_in: ${expires} (original value: ${requestBody.expires_in})`);
|
|
2216
2284
|
return expires;
|
|
2217
2285
|
}
|
|
2218
2286
|
}
|
|
2219
|
-
function prepareRequestOptions$2(
|
|
2287
|
+
function prepareRequestOptions$2(scopes, clientId) {
|
|
2220
2288
|
var _a;
|
|
2289
|
+
const resource = mapScopesToResource(scopes);
|
|
2290
|
+
if (!resource) {
|
|
2291
|
+
throw new Error(`${msiName$2}: Multiple scopes are not supported.`);
|
|
2292
|
+
}
|
|
2221
2293
|
const queryParameters = {
|
|
2222
2294
|
resource,
|
|
2223
2295
|
"api-version": imdsApiVersion
|
|
@@ -2225,7 +2297,8 @@ function prepareRequestOptions$2(resource, clientId) {
|
|
|
2225
2297
|
if (clientId) {
|
|
2226
2298
|
queryParameters.client_id = clientId;
|
|
2227
2299
|
}
|
|
2228
|
-
const
|
|
2300
|
+
const params = new URLSearchParams(queryParameters);
|
|
2301
|
+
const query = params.toString();
|
|
2229
2302
|
const url = new URL(imdsEndpointPath, (_a = process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : imdsHost);
|
|
2230
2303
|
return {
|
|
2231
2304
|
url: `${url}?${query}`,
|
|
@@ -2243,8 +2316,13 @@ const imdsMsiRetryConfig = {
|
|
|
2243
2316
|
intervalIncrement: 2
|
|
2244
2317
|
};
|
|
2245
2318
|
const imdsMsi = {
|
|
2246
|
-
async isAvailable(
|
|
2319
|
+
async isAvailable(scopes, identityClient, clientId, getTokenOptions) {
|
|
2247
2320
|
var _a, _b;
|
|
2321
|
+
const resource = mapScopesToResource(scopes);
|
|
2322
|
+
if (!resource) {
|
|
2323
|
+
logger$b.info(`${msiName$2}: Unavailable. Multiple scopes are not supported.`);
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2248
2326
|
const { span, updatedOptions: options } = createSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions);
|
|
2249
2327
|
// if the PodIdenityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist
|
|
2250
2328
|
if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {
|
|
@@ -2257,10 +2335,7 @@ const imdsMsi = {
|
|
|
2257
2335
|
// IMDS endpoint
|
|
2258
2336
|
requestOptions.headers.delete("Metadata");
|
|
2259
2337
|
}
|
|
2260
|
-
requestOptions.tracingOptions =
|
|
2261
|
-
spanOptions: options.tracingOptions && options.tracingOptions.spanOptions,
|
|
2262
|
-
tracingContext: options.tracingOptions && options.tracingOptions.tracingContext
|
|
2263
|
-
};
|
|
2338
|
+
requestOptions.tracingOptions = options.tracingOptions;
|
|
2264
2339
|
try {
|
|
2265
2340
|
// Create a request with a timeout since we expect that
|
|
2266
2341
|
// not having a "Metadata" header should cause an error to be
|
|
@@ -2270,18 +2345,19 @@ const imdsMsi = {
|
|
|
2270
2345
|
// This MSI uses the imdsEndpoint to get the token, which only uses http://
|
|
2271
2346
|
request.allowInsecureConnection = true;
|
|
2272
2347
|
try {
|
|
2273
|
-
logger$b.info(
|
|
2348
|
+
logger$b.info(`${msiName$2}: Pinging the Azure IMDS endpoint`);
|
|
2274
2349
|
await identityClient.sendRequest(request);
|
|
2275
2350
|
}
|
|
2276
2351
|
catch (err) {
|
|
2277
2352
|
if ((err.name === "RestError" && err.code === coreRestPipeline.RestError.REQUEST_SEND_ERROR) ||
|
|
2278
2353
|
err.name === "AbortError" ||
|
|
2354
|
+
err.code === "ENETUNREACH" || // Network unreachable
|
|
2279
2355
|
err.code === "ECONNREFUSED" || // connection refused
|
|
2280
2356
|
err.code === "EHOSTDOWN" // host is down
|
|
2281
2357
|
) {
|
|
2282
2358
|
// If the request failed, or Node.js was unable to establish a connection,
|
|
2283
2359
|
// or the host was down, we'll assume the IMDS endpoint isn't available.
|
|
2284
|
-
logger$b.info(
|
|
2360
|
+
logger$b.info(`${msiName$2}: The Azure IMDS endpoint is unavailable`);
|
|
2285
2361
|
span.setStatus({
|
|
2286
2362
|
code: coreTracing.SpanStatusCode.ERROR,
|
|
2287
2363
|
message: err.message
|
|
@@ -2290,13 +2366,13 @@ const imdsMsi = {
|
|
|
2290
2366
|
}
|
|
2291
2367
|
}
|
|
2292
2368
|
// If we received any response, the endpoint is available
|
|
2293
|
-
logger$b.info(
|
|
2369
|
+
logger$b.info(`${msiName$2}: The Azure IMDS endpoint is available`);
|
|
2294
2370
|
return true;
|
|
2295
2371
|
}
|
|
2296
2372
|
catch (err) {
|
|
2297
2373
|
// createWebResource failed.
|
|
2298
2374
|
// This error should bubble up to the user.
|
|
2299
|
-
logger$b.info(
|
|
2375
|
+
logger$b.info(`${msiName$2}: Error when creating the WebResource for the Azure IMDS endpoint: ${err.message}`);
|
|
2300
2376
|
span.setStatus({
|
|
2301
2377
|
code: coreTracing.SpanStatusCode.ERROR,
|
|
2302
2378
|
message: err.message
|
|
@@ -2307,12 +2383,13 @@ const imdsMsi = {
|
|
|
2307
2383
|
span.end();
|
|
2308
2384
|
}
|
|
2309
2385
|
},
|
|
2310
|
-
async getToken(
|
|
2311
|
-
|
|
2386
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2387
|
+
const { identityClient, scopes, clientId } = configuration;
|
|
2388
|
+
logger$b.info(`${msiName$2}: Using the Azure IMDS endpoint coming from the environment variable MSI_ENDPOINT=${process.env.MSI_ENDPOINT}, and using the cloud shell to proceed with the authentication.`);
|
|
2312
2389
|
let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;
|
|
2313
2390
|
for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {
|
|
2314
2391
|
try {
|
|
2315
|
-
return await msiGenericGetToken(identityClient, prepareRequestOptions$2(
|
|
2392
|
+
return await msiGenericGetToken(identityClient, prepareRequestOptions$2(scopes, clientId), expiresInParser$2, getTokenOptions);
|
|
2316
2393
|
}
|
|
2317
2394
|
catch (error) {
|
|
2318
2395
|
if (error.statusCode === 404) {
|
|
@@ -2323,15 +2400,20 @@ const imdsMsi = {
|
|
|
2323
2400
|
throw error;
|
|
2324
2401
|
}
|
|
2325
2402
|
}
|
|
2326
|
-
throw new AuthenticationError(404,
|
|
2403
|
+
throw new AuthenticationError(404, `${msiName$2}: Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`);
|
|
2327
2404
|
}
|
|
2328
2405
|
};
|
|
2329
2406
|
|
|
2330
2407
|
// Copyright (c) Microsoft Corporation.
|
|
2331
|
-
const
|
|
2408
|
+
const msiName$3 = "ManagedIdentityCredential - Azure Arc MSI";
|
|
2409
|
+
const logger$c = credentialLogger(msiName$3);
|
|
2332
2410
|
// Azure Arc MSI doesn't have a special expiresIn parser.
|
|
2333
2411
|
const expiresInParser$3 = undefined;
|
|
2334
|
-
function prepareRequestOptions$3(
|
|
2412
|
+
function prepareRequestOptions$3(scopes) {
|
|
2413
|
+
const resource = mapScopesToResource(scopes);
|
|
2414
|
+
if (!resource) {
|
|
2415
|
+
throw new Error(`${msiName$3}: Multiple scopes are not supported.`);
|
|
2416
|
+
}
|
|
2335
2417
|
const queryParameters = {
|
|
2336
2418
|
resource,
|
|
2337
2419
|
"api-version": azureArcAPIVersion
|
|
@@ -2339,7 +2421,7 @@ function prepareRequestOptions$3(resource) {
|
|
|
2339
2421
|
const query = new URLSearchParams(queryParameters);
|
|
2340
2422
|
// This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below.
|
|
2341
2423
|
if (!process.env.IDENTITY_ENDPOINT) {
|
|
2342
|
-
throw new Error(
|
|
2424
|
+
throw new Error(`${msiName$3}: Missing environment variable: IDENTITY_ENDPOINT`);
|
|
2343
2425
|
}
|
|
2344
2426
|
return coreRestPipeline.createPipelineRequest({
|
|
2345
2427
|
// Should be similar to: http://localhost:40342/metadata/identity/oauth2/token
|
|
@@ -2352,7 +2434,7 @@ function prepareRequestOptions$3(resource) {
|
|
|
2352
2434
|
});
|
|
2353
2435
|
}
|
|
2354
2436
|
// Since "fs"'s readFileSync locks the thread, and to avoid extra dependencies.
|
|
2355
|
-
function readFileAsync(path, options) {
|
|
2437
|
+
function readFileAsync$1(path, options) {
|
|
2356
2438
|
return new Promise((resolve, reject) => fs.readFile(path, options, (err, data) => {
|
|
2357
2439
|
if (err) {
|
|
2358
2440
|
reject(err);
|
|
@@ -2367,7 +2449,7 @@ async function filePathRequest(identityClient, requestPrepareOptions) {
|
|
|
2367
2449
|
if (response.bodyAsText) {
|
|
2368
2450
|
message = ` Response: ${response.bodyAsText}`;
|
|
2369
2451
|
}
|
|
2370
|
-
throw new AuthenticationError(response.status,
|
|
2452
|
+
throw new AuthenticationError(response.status, `${msiName$3}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`);
|
|
2371
2453
|
}
|
|
2372
2454
|
const authHeader = response.headers.get("www-authenticate") || "";
|
|
2373
2455
|
try {
|
|
@@ -2378,32 +2460,113 @@ async function filePathRequest(identityClient, requestPrepareOptions) {
|
|
|
2378
2460
|
}
|
|
2379
2461
|
}
|
|
2380
2462
|
const arcMsi = {
|
|
2381
|
-
async isAvailable() {
|
|
2463
|
+
async isAvailable(scopes) {
|
|
2464
|
+
const resource = mapScopesToResource(scopes);
|
|
2465
|
+
if (!resource) {
|
|
2466
|
+
logger$c.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`);
|
|
2467
|
+
return false;
|
|
2468
|
+
}
|
|
2382
2469
|
const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT);
|
|
2383
2470
|
if (!result) {
|
|
2384
|
-
logger$c.info(
|
|
2471
|
+
logger$c.info(`${msiName$3}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`);
|
|
2385
2472
|
}
|
|
2386
2473
|
return result;
|
|
2387
2474
|
},
|
|
2388
|
-
async getToken(
|
|
2475
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2389
2476
|
var _a;
|
|
2390
|
-
|
|
2477
|
+
const { identityClient, scopes, clientId } = configuration;
|
|
2478
|
+
logger$c.info(`${msiName$3}: Authenticating.`);
|
|
2391
2479
|
if (clientId) {
|
|
2392
|
-
throw new Error(
|
|
2480
|
+
throw new Error(`${msiName$3}: User assigned identity is not supported by the Azure Arc Managed Identity Endpoint. To authenticate with the system assigned identity, omit the client id when constructing the ManagedIdentityCredential, or if authenticating with the DefaultAzureCredential ensure the AZURE_CLIENT_ID environment variable is not set.`);
|
|
2393
2481
|
}
|
|
2394
|
-
const requestOptions = Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal
|
|
2482
|
+
const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes)), { allowInsecureConnection: true });
|
|
2395
2483
|
const filePath = await filePathRequest(identityClient, requestOptions);
|
|
2396
2484
|
if (!filePath) {
|
|
2397
|
-
throw new Error(
|
|
2485
|
+
throw new Error(`${msiName$3}: Failed to find the token file.`);
|
|
2398
2486
|
}
|
|
2399
|
-
const key = await readFileAsync(filePath, { encoding: "utf-8" });
|
|
2487
|
+
const key = await readFileAsync$1(filePath, { encoding: "utf-8" });
|
|
2400
2488
|
(_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`);
|
|
2401
2489
|
return msiGenericGetToken(identityClient, requestOptions, expiresInParser$3, getTokenOptions);
|
|
2402
2490
|
}
|
|
2403
2491
|
};
|
|
2404
2492
|
|
|
2405
2493
|
// Copyright (c) Microsoft Corporation.
|
|
2406
|
-
const
|
|
2494
|
+
const msiName$4 = "ManagedIdentityCredential - Token Exchange";
|
|
2495
|
+
const logger$d = credentialLogger(msiName$4);
|
|
2496
|
+
const readFileAsync$2 = util.promisify(fs__default.readFile);
|
|
2497
|
+
function expiresInParser$4(requestBody) {
|
|
2498
|
+
// Parses a string representation of the seconds since epoch into a number value
|
|
2499
|
+
return Number(requestBody.expires_on);
|
|
2500
|
+
}
|
|
2501
|
+
function prepareRequestOptions$4(scopes, clientAssertion, clientId) {
|
|
2502
|
+
var _a;
|
|
2503
|
+
const bodyParams = {
|
|
2504
|
+
scope: Array.isArray(scopes) ? scopes.join(" ") : scopes,
|
|
2505
|
+
client_assertion: clientAssertion,
|
|
2506
|
+
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
2507
|
+
client_id: clientId,
|
|
2508
|
+
grant_type: "client_credentials"
|
|
2509
|
+
};
|
|
2510
|
+
const urlParams = new URLSearchParams(bodyParams);
|
|
2511
|
+
const url = new URL(`${process.env.AZURE_TENANT_ID}/oauth2/v2.0/token`, (_a = process.env.AZURE_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : DefaultAuthorityHost);
|
|
2512
|
+
return {
|
|
2513
|
+
url: url.toString(),
|
|
2514
|
+
method: "POST",
|
|
2515
|
+
body: urlParams.toString(),
|
|
2516
|
+
headers: coreRestPipeline.createHttpHeaders({
|
|
2517
|
+
Accept: "application/json"
|
|
2518
|
+
})
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
function tokenExchangeMsi() {
|
|
2522
|
+
const azureFederatedTokenFilePath = process.env.AZURE_FEDERATED_TOKEN_FILE;
|
|
2523
|
+
let azureFederatedTokenFileContent = undefined;
|
|
2524
|
+
let cacheDate = undefined;
|
|
2525
|
+
// Only reads from the assertion file once every 5 minutes
|
|
2526
|
+
async function readAssertion() {
|
|
2527
|
+
// Cached assertions expire after 5 minutes
|
|
2528
|
+
if (cacheDate !== undefined && Date.now() - cacheDate >= 1000 * 60 * 5) {
|
|
2529
|
+
azureFederatedTokenFileContent = undefined;
|
|
2530
|
+
}
|
|
2531
|
+
if (!azureFederatedTokenFileContent) {
|
|
2532
|
+
const file = await readFileAsync$2(azureFederatedTokenFilePath, "utf8");
|
|
2533
|
+
const value = file.trim();
|
|
2534
|
+
if (!value) {
|
|
2535
|
+
throw new Error(`No content on the file ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
|
|
2536
|
+
}
|
|
2537
|
+
else {
|
|
2538
|
+
azureFederatedTokenFileContent = value;
|
|
2539
|
+
cacheDate = Date.now();
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
return azureFederatedTokenFileContent;
|
|
2543
|
+
}
|
|
2544
|
+
return {
|
|
2545
|
+
async isAvailable(_scopes, _identityClient, clientId) {
|
|
2546
|
+
const env = process.env;
|
|
2547
|
+
const result = Boolean((clientId || env.AZURE_CLIENT_ID) && env.AZURE_TENANT_ID && azureFederatedTokenFilePath);
|
|
2548
|
+
if (!result) {
|
|
2549
|
+
logger$d.info(`${msiName$4}: Unavailable. The environment variables needed are: AZURE_CLIENT_ID (or the client ID sent through the parameters), AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE`);
|
|
2550
|
+
}
|
|
2551
|
+
return result;
|
|
2552
|
+
},
|
|
2553
|
+
async getToken(configuration, getTokenOptions = {}) {
|
|
2554
|
+
const { identityClient, scopes, clientId } = configuration;
|
|
2555
|
+
logger$d.info(`${msiName$4}: Using the client assertion coming from environment variables.`);
|
|
2556
|
+
let assertion;
|
|
2557
|
+
try {
|
|
2558
|
+
assertion = await readAssertion();
|
|
2559
|
+
}
|
|
2560
|
+
catch (err) {
|
|
2561
|
+
throw new Error(`${msiName$4}: Failed to read ${azureFederatedTokenFilePath}, indicated by the environment variable AZURE_FEDERATED_TOKEN_FILE`);
|
|
2562
|
+
}
|
|
2563
|
+
return msiGenericGetToken(identityClient, prepareRequestOptions$4(scopes, assertion, clientId || process.env.AZURE_CLIENT_ID), expiresInParser$4, getTokenOptions);
|
|
2564
|
+
}
|
|
2565
|
+
};
|
|
2566
|
+
}
|
|
2567
|
+
|
|
2568
|
+
// Copyright (c) Microsoft Corporation.
|
|
2569
|
+
const logger$e = credentialLogger("ManagedIdentityCredential");
|
|
2407
2570
|
/**
|
|
2408
2571
|
* Attempts authentication using a managed identity that has been assigned
|
|
2409
2572
|
* to the deployment environment. This authentication type works in Azure VMs,
|
|
@@ -2430,15 +2593,15 @@ class ManagedIdentityCredential {
|
|
|
2430
2593
|
this.identityClient = new IdentityClient(clientIdOrOptions);
|
|
2431
2594
|
}
|
|
2432
2595
|
}
|
|
2433
|
-
async cachedAvailableMSI(
|
|
2596
|
+
async cachedAvailableMSI(scopes, clientId, getTokenOptions) {
|
|
2434
2597
|
if (this.cachedMSI) {
|
|
2435
2598
|
return this.cachedMSI;
|
|
2436
2599
|
}
|
|
2437
2600
|
// "fabricMsi" can't be added yet because our HTTPs pipeline doesn't allow skipping the SSL verification step,
|
|
2438
2601
|
// which is necessary since Service Fabric only provides self-signed certificates on their Identity Endpoint.
|
|
2439
|
-
const MSIs = [appServiceMsi2017, cloudShellMsi, arcMsi, imdsMsi];
|
|
2602
|
+
const MSIs = [appServiceMsi2017, cloudShellMsi, arcMsi, tokenExchangeMsi(), imdsMsi];
|
|
2440
2603
|
for (const msi of MSIs) {
|
|
2441
|
-
if (await msi.isAvailable(this.identityClient,
|
|
2604
|
+
if (await msi.isAvailable(scopes, this.identityClient, clientId, getTokenOptions)) {
|
|
2442
2605
|
this.cachedMSI = msi;
|
|
2443
2606
|
return msi;
|
|
2444
2607
|
}
|
|
@@ -2446,12 +2609,15 @@ class ManagedIdentityCredential {
|
|
|
2446
2609
|
throw new CredentialUnavailableError("ManagedIdentityCredential - No MSI credential available");
|
|
2447
2610
|
}
|
|
2448
2611
|
async authenticateManagedIdentity(scopes, clientId, getTokenOptions) {
|
|
2449
|
-
const resource = mapScopesToResource(scopes);
|
|
2450
2612
|
const { span, updatedOptions } = createSpan("ManagedIdentityCredential-authenticateManagedIdentity", getTokenOptions);
|
|
2451
2613
|
try {
|
|
2452
2614
|
// Determining the available MSI, and avoiding checking for other MSIs while the program is running.
|
|
2453
|
-
const availableMSI = await this.cachedAvailableMSI(
|
|
2454
|
-
return availableMSI.getToken(
|
|
2615
|
+
const availableMSI = await this.cachedAvailableMSI(scopes, clientId, updatedOptions);
|
|
2616
|
+
return availableMSI.getToken({
|
|
2617
|
+
identityClient: this.identityClient,
|
|
2618
|
+
scopes,
|
|
2619
|
+
clientId
|
|
2620
|
+
}, updatedOptions);
|
|
2455
2621
|
}
|
|
2456
2622
|
catch (err) {
|
|
2457
2623
|
span.setStatus({
|
|
@@ -2490,7 +2656,7 @@ class ManagedIdentityCredential {
|
|
|
2490
2656
|
// It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method),
|
|
2491
2657
|
// yet we had no access token. For this reason, we'll throw once with a specific message:
|
|
2492
2658
|
const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received.");
|
|
2493
|
-
logger$
|
|
2659
|
+
logger$e.getToken.info(formatError(scopes, error));
|
|
2494
2660
|
throw error;
|
|
2495
2661
|
}
|
|
2496
2662
|
// Since `authenticateManagedIdentity` didn't throw, and the result was not null,
|
|
@@ -2502,10 +2668,10 @@ class ManagedIdentityCredential {
|
|
|
2502
2668
|
// We've previously determined that the endpoint was unavailable,
|
|
2503
2669
|
// either because it was unreachable or permanently unable to authenticate.
|
|
2504
2670
|
const error = new CredentialUnavailableError("The managed identity endpoint is not currently available");
|
|
2505
|
-
logger$
|
|
2671
|
+
logger$e.getToken.info(formatError(scopes, error));
|
|
2506
2672
|
throw error;
|
|
2507
2673
|
}
|
|
2508
|
-
logger$
|
|
2674
|
+
logger$e.getToken.info(formatSuccess(scopes));
|
|
2509
2675
|
return result;
|
|
2510
2676
|
}
|
|
2511
2677
|
catch (err) {
|
|
@@ -2526,21 +2692,21 @@ class ManagedIdentityCredential {
|
|
|
2526
2692
|
// If either the network is unreachable,
|
|
2527
2693
|
// we can safely assume the credential is unavailable.
|
|
2528
2694
|
if (err.code === "ENETUNREACH") {
|
|
2529
|
-
const error = new CredentialUnavailableError(
|
|
2530
|
-
logger$
|
|
2695
|
+
const error = new CredentialUnavailableError(`ManagedIdentityCredential is unavailable. Network unreachable. Message: ${err.message}`);
|
|
2696
|
+
logger$e.getToken.info(formatError(scopes, error));
|
|
2531
2697
|
throw error;
|
|
2532
2698
|
}
|
|
2533
2699
|
// If either the host was unreachable,
|
|
2534
2700
|
// we can safely assume the credential is unavailable.
|
|
2535
2701
|
if (err.code === "EHOSTUNREACH") {
|
|
2536
|
-
const error = new CredentialUnavailableError(
|
|
2537
|
-
logger$
|
|
2702
|
+
const error = new CredentialUnavailableError(`ManagedIdentityCredential is unavailable. No managed identity endpoint found. Message: ${err.message}`);
|
|
2703
|
+
logger$e.getToken.info(formatError(scopes, error));
|
|
2538
2704
|
throw error;
|
|
2539
2705
|
}
|
|
2540
2706
|
// If err.statusCode has a value of 400, it comes from sendTokenRequest,
|
|
2541
2707
|
// and it means that the endpoint is working, but that no identity is available.
|
|
2542
2708
|
if (err.statusCode === 400) {
|
|
2543
|
-
throw new CredentialUnavailableError(
|
|
2709
|
+
throw new CredentialUnavailableError(`ManagedIdentityCredential: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`);
|
|
2544
2710
|
}
|
|
2545
2711
|
// If the error has no status code, we can assume there was no available identity.
|
|
2546
2712
|
// This will throw silently during any ChainedTokenCredential.
|
|
@@ -2599,19 +2765,16 @@ const defaultCredentials = [
|
|
|
2599
2765
|
*
|
|
2600
2766
|
* Consult the documentation of these credential types for more information
|
|
2601
2767
|
* on how they attempt authentication.
|
|
2602
|
-
*
|
|
2603
|
-
* **Note**: `VisualStudioCodeCredential` is provided by an extension package:
|
|
2604
|
-
* `@azure/identity-vscode`. If this package is not installed and registered
|
|
2605
|
-
* using the extension API (`useIdentityExtension`), then authentication using
|
|
2606
|
-
* `VisualStudioCodeCredential` will not be available.
|
|
2607
|
-
*
|
|
2608
|
-
* Azure Identity extensions may add credential types to the default credential
|
|
2609
|
-
* stack.
|
|
2610
2768
|
*/
|
|
2611
2769
|
class DefaultAzureCredential extends ChainedTokenCredential {
|
|
2612
2770
|
/**
|
|
2613
2771
|
* Creates an instance of the DefaultAzureCredential class.
|
|
2614
2772
|
*
|
|
2773
|
+
* **Note**: `VisualStudioCodeCredential` is provided by a plugin package:
|
|
2774
|
+
* `@azure/identity-vscode`. If this package is not installed and registered
|
|
2775
|
+
* using the plugin API (`useIdentityPlugin`), then authentication using
|
|
2776
|
+
* `VisualStudioCodeCredential` will not be available.
|
|
2777
|
+
*
|
|
2615
2778
|
* @param options - Optional parameters. See {@link DefaultAzureCredentialOptions}.
|
|
2616
2779
|
*/
|
|
2617
2780
|
constructor(options) {
|
|
@@ -2765,7 +2928,7 @@ class MsalOpenBrowser extends MsalNode {
|
|
|
2765
2928
|
}
|
|
2766
2929
|
|
|
2767
2930
|
// Copyright (c) Microsoft Corporation.
|
|
2768
|
-
const logger$
|
|
2931
|
+
const logger$f = credentialLogger("InteractiveBrowserCredential");
|
|
2769
2932
|
/**
|
|
2770
2933
|
* Enables authentication to Azure Active Directory inside of the web browser
|
|
2771
2934
|
* using the interactive login flow.
|
|
@@ -2787,7 +2950,7 @@ class InteractiveBrowserCredential {
|
|
|
2787
2950
|
const redirectUri = typeof options.redirectUri === "function"
|
|
2788
2951
|
? options.redirectUri()
|
|
2789
2952
|
: options.redirectUri || "http://localhost";
|
|
2790
|
-
this.msalFlow = new MsalOpenBrowser(Object.assign(Object.assign({}, options), { tokenCredentialOptions: options, logger: logger$
|
|
2953
|
+
this.msalFlow = new MsalOpenBrowser(Object.assign(Object.assign({}, options), { tokenCredentialOptions: options, logger: logger$f,
|
|
2791
2954
|
redirectUri }));
|
|
2792
2955
|
this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
|
|
2793
2956
|
}
|
|
@@ -2865,7 +3028,7 @@ class MsalDeviceCode extends MsalNode {
|
|
|
2865
3028
|
}
|
|
2866
3029
|
|
|
2867
3030
|
// Copyright (c) Microsoft Corporation.
|
|
2868
|
-
const logger$
|
|
3031
|
+
const logger$g = credentialLogger("DeviceCodeCredential");
|
|
2869
3032
|
/**
|
|
2870
3033
|
* Method that logs the user code from the DeviceCodeCredential.
|
|
2871
3034
|
* @param deviceCodeInfo - The device code.
|
|
@@ -2885,7 +3048,7 @@ class DeviceCodeCredential {
|
|
|
2885
3048
|
* @param options - Options for configuring the client which makes the authentication requests.
|
|
2886
3049
|
*/
|
|
2887
3050
|
constructor(options) {
|
|
2888
|
-
this.msalFlow = new MsalDeviceCode(Object.assign(Object.assign({}, options), { logger: logger$
|
|
3051
|
+
this.msalFlow = new MsalDeviceCode(Object.assign(Object.assign({}, options), { logger: logger$g, userPromptCallback: (options === null || options === void 0 ? void 0 : options.userPromptCallback) || defaultDeviceCodePromptCallback, tokenCredentialOptions: options || {} }));
|
|
2889
3052
|
this.disableAutomaticAuthentication = options === null || options === void 0 ? void 0 : options.disableAutomaticAuthentication;
|
|
2890
3053
|
}
|
|
2891
3054
|
/**
|
|
@@ -2926,7 +3089,45 @@ class DeviceCodeCredential {
|
|
|
2926
3089
|
}
|
|
2927
3090
|
|
|
2928
3091
|
// Copyright (c) Microsoft Corporation.
|
|
2929
|
-
|
|
3092
|
+
/**
|
|
3093
|
+
* This MSAL client sets up a web server to listen for redirect callbacks, then calls to the MSAL's public application's `acquireTokenByDeviceCode` during `doGetToken`
|
|
3094
|
+
* to trigger the authentication flow, and then respond based on the values obtained from the redirect callback
|
|
3095
|
+
* @internal
|
|
3096
|
+
*/
|
|
3097
|
+
class MsalAuthorizationCode extends MsalNode {
|
|
3098
|
+
constructor(options) {
|
|
3099
|
+
super(options);
|
|
3100
|
+
this.logger = credentialLogger("NodeJS MSAL Authorization Code");
|
|
3101
|
+
this.redirectUri = options.redirectUri;
|
|
3102
|
+
this.authorizationCode = options.authorizationCode;
|
|
3103
|
+
if (options.clientSecret) {
|
|
3104
|
+
this.msalConfig.auth.clientSecret = options.clientSecret;
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
async getAuthCodeUrl(options) {
|
|
3108
|
+
await this.init();
|
|
3109
|
+
return this.confidentialApp.getAuthCodeUrl(options);
|
|
3110
|
+
}
|
|
3111
|
+
async doGetToken(scopes, options) {
|
|
3112
|
+
var _a;
|
|
3113
|
+
try {
|
|
3114
|
+
const result = await ((_a = this.confidentialApp) === null || _a === void 0 ? void 0 : _a.acquireTokenByCode({
|
|
3115
|
+
scopes,
|
|
3116
|
+
redirectUri: this.redirectUri,
|
|
3117
|
+
code: this.authorizationCode
|
|
3118
|
+
}));
|
|
3119
|
+
// The Client Credential flow does not return an account,
|
|
3120
|
+
// so each time getToken gets called, we will have to acquire a new token through the service.
|
|
3121
|
+
return this.handleResult(scopes, this.clientId, result || undefined);
|
|
3122
|
+
}
|
|
3123
|
+
catch (err) {
|
|
3124
|
+
throw this.handleError(scopes, err, options);
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
|
|
3129
|
+
// Copyright (c) Microsoft Corporation.
|
|
3130
|
+
const logger$h = credentialLogger("AuthorizationCodeCredential");
|
|
2930
3131
|
/**
|
|
2931
3132
|
* Enables authentication to Azure Active Directory using an authorization code
|
|
2932
3133
|
* that was obtained through the authorization code flow, described in more detail
|
|
@@ -2940,26 +3141,23 @@ class AuthorizationCodeCredential {
|
|
|
2940
3141
|
* @internal
|
|
2941
3142
|
*/
|
|
2942
3143
|
constructor(tenantId, clientId, clientSecretOrAuthorizationCode, authorizationCodeOrRedirectUri, redirectUriOrOptions, options) {
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
this.clientId = clientId;
|
|
2946
|
-
this.tenantId = tenantId;
|
|
3144
|
+
checkTenantId(logger$h, tenantId);
|
|
3145
|
+
let clientSecret = clientSecretOrAuthorizationCode;
|
|
2947
3146
|
if (typeof redirectUriOrOptions === "string") {
|
|
2948
3147
|
// the clientId+clientSecret constructor
|
|
2949
|
-
this.clientSecret = clientSecretOrAuthorizationCode;
|
|
2950
3148
|
this.authorizationCode = authorizationCodeOrRedirectUri;
|
|
2951
3149
|
this.redirectUri = redirectUriOrOptions;
|
|
2952
3150
|
// options okay
|
|
2953
3151
|
}
|
|
2954
3152
|
else {
|
|
2955
3153
|
// clientId only
|
|
2956
|
-
this.clientSecret = undefined;
|
|
2957
3154
|
this.authorizationCode = clientSecretOrAuthorizationCode;
|
|
2958
3155
|
this.redirectUri = authorizationCodeOrRedirectUri;
|
|
3156
|
+
clientSecret = undefined;
|
|
2959
3157
|
options = redirectUriOrOptions;
|
|
2960
3158
|
}
|
|
2961
|
-
this.
|
|
2962
|
-
|
|
3159
|
+
this.msalFlow = new MsalAuthorizationCode(Object.assign(Object.assign({}, options), { clientSecret,
|
|
3160
|
+
clientId, tokenCredentialOptions: options || {}, logger: logger$h, redirectUri: this.redirectUri, authorizationCode: this.authorizationCode }));
|
|
2963
3161
|
}
|
|
2964
3162
|
/**
|
|
2965
3163
|
* Authenticates with Azure Active Directory and returns an access token if successful.
|
|
@@ -2969,67 +3167,11 @@ class AuthorizationCodeCredential {
|
|
|
2969
3167
|
* @param options - The options used to configure any requests this
|
|
2970
3168
|
* TokenCredential implementation might make.
|
|
2971
3169
|
*/
|
|
2972
|
-
async getToken(scopes, options) {
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
this.
|
|
2976
|
-
|
|
2977
|
-
try {
|
|
2978
|
-
let tokenResponse = null;
|
|
2979
|
-
let scopeString = typeof scopes === "string" ? scopes : scopes.join(" ");
|
|
2980
|
-
if (scopeString.indexOf("offline_access") < 0) {
|
|
2981
|
-
scopeString += " offline_access";
|
|
2982
|
-
}
|
|
2983
|
-
// Try to use the refresh token first
|
|
2984
|
-
if (this.lastTokenResponse && this.lastTokenResponse.refreshToken) {
|
|
2985
|
-
tokenResponse = await this.identityClient.refreshAccessToken(tenantId, this.clientId, scopeString, this.lastTokenResponse.refreshToken, this.clientSecret, undefined, updatedOptions);
|
|
2986
|
-
}
|
|
2987
|
-
const query = new URLSearchParams({
|
|
2988
|
-
client_id: this.clientId,
|
|
2989
|
-
grant_type: "authorization_code",
|
|
2990
|
-
scope: scopeString,
|
|
2991
|
-
code: this.authorizationCode,
|
|
2992
|
-
redirect_uri: this.redirectUri
|
|
2993
|
-
});
|
|
2994
|
-
if (this.clientSecret) {
|
|
2995
|
-
query.set("client_secret", this.clientSecret);
|
|
2996
|
-
}
|
|
2997
|
-
if (tokenResponse === null) {
|
|
2998
|
-
const urlSuffix = getIdentityTokenEndpointSuffix(tenantId);
|
|
2999
|
-
const pipelineRequest = coreRestPipeline.createPipelineRequest({
|
|
3000
|
-
url: `${this.identityClient.authorityHost}/${tenantId}/${urlSuffix}`,
|
|
3001
|
-
method: "POST",
|
|
3002
|
-
body: query.toString(),
|
|
3003
|
-
headers: coreRestPipeline.createHttpHeaders({
|
|
3004
|
-
Accept: "application/json",
|
|
3005
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
3006
|
-
}),
|
|
3007
|
-
tracingOptions: {
|
|
3008
|
-
spanOptions: (_a = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _a === void 0 ? void 0 : _a.spanOptions,
|
|
3009
|
-
tracingContext: (_b = updatedOptions === null || updatedOptions === void 0 ? void 0 : updatedOptions.tracingOptions) === null || _b === void 0 ? void 0 : _b.tracingContext
|
|
3010
|
-
}
|
|
3011
|
-
});
|
|
3012
|
-
tokenResponse = await this.identityClient.sendTokenRequest(pipelineRequest, (response) => new Date(response === null || response === void 0 ? void 0 : response.expires_on).getTime());
|
|
3013
|
-
}
|
|
3014
|
-
this.lastTokenResponse = tokenResponse;
|
|
3015
|
-
logger$g.getToken.info(formatSuccess(scopes));
|
|
3016
|
-
const token = tokenResponse && tokenResponse.accessToken;
|
|
3017
|
-
if (!token) {
|
|
3018
|
-
throw new CredentialUnavailableError("Failed to retrieve a valid token");
|
|
3019
|
-
}
|
|
3020
|
-
return token;
|
|
3021
|
-
}
|
|
3022
|
-
catch (err) {
|
|
3023
|
-
span.setStatus({
|
|
3024
|
-
code: coreTracing.SpanStatusCode.ERROR,
|
|
3025
|
-
message: err.message
|
|
3026
|
-
});
|
|
3027
|
-
logger$g.getToken.info(formatError(scopes, err));
|
|
3028
|
-
throw err;
|
|
3029
|
-
}
|
|
3030
|
-
finally {
|
|
3031
|
-
span.end();
|
|
3032
|
-
}
|
|
3170
|
+
async getToken(scopes, options = {}) {
|
|
3171
|
+
return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
|
|
3172
|
+
const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
|
|
3173
|
+
return this.msalFlow.getToken(arrayScopes, Object.assign(Object.assign({}, newOptions), { disableAutomaticAuthentication: this.disableAutomaticAuthentication }));
|
|
3174
|
+
});
|
|
3033
3175
|
}
|
|
3034
3176
|
}
|
|
3035
3177
|
|
|
@@ -3049,9 +3191,6 @@ const ApplicationCredentials = [
|
|
|
3049
3191
|
*
|
|
3050
3192
|
* Consult the documentation of these credential types for more information
|
|
3051
3193
|
* on how they attempt authentication.
|
|
3052
|
-
*
|
|
3053
|
-
* Azure Identity extensions may add credential types to the default credential
|
|
3054
|
-
* stack.
|
|
3055
3194
|
*/
|
|
3056
3195
|
class ApplicationCredential extends ChainedTokenCredential {
|
|
3057
3196
|
/**
|
|
@@ -3066,6 +3205,116 @@ class ApplicationCredential extends ChainedTokenCredential {
|
|
|
3066
3205
|
}
|
|
3067
3206
|
}
|
|
3068
3207
|
|
|
3208
|
+
// Copyright (c) Microsoft Corporation.
|
|
3209
|
+
/**
|
|
3210
|
+
* MSAL on behalf of flow. Calls to MSAL's confidential application's `acquireTokenOnBehalfOf` during `doGetToken`.
|
|
3211
|
+
* @internal
|
|
3212
|
+
*/
|
|
3213
|
+
class MsalOnBehalfOf extends MsalNode {
|
|
3214
|
+
constructor(options) {
|
|
3215
|
+
super(options);
|
|
3216
|
+
this.logger.info("Initialized MSAL's On-Behalf-Of flow");
|
|
3217
|
+
this.requiresConfidential = true;
|
|
3218
|
+
this.userAssertionToken = options.userAssertionToken;
|
|
3219
|
+
this.certificatePath = options.certificatePath;
|
|
3220
|
+
this.sendCertificateChain = options.sendCertificateChain;
|
|
3221
|
+
this.clientSecret = options.clientSecret;
|
|
3222
|
+
}
|
|
3223
|
+
// Changing the MSAL configuration asynchronously
|
|
3224
|
+
async init(options) {
|
|
3225
|
+
if (this.certificatePath) {
|
|
3226
|
+
try {
|
|
3227
|
+
const parts = await parseCertificate(this.certificatePath, this.sendCertificateChain);
|
|
3228
|
+
this.msalConfig.auth.clientCertificate = {
|
|
3229
|
+
thumbprint: parts.thumbprint,
|
|
3230
|
+
privateKey: parts.certificateContents,
|
|
3231
|
+
x5c: parts.x5c
|
|
3232
|
+
};
|
|
3233
|
+
}
|
|
3234
|
+
catch (error) {
|
|
3235
|
+
this.logger.info(formatError("", error));
|
|
3236
|
+
throw error;
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
else {
|
|
3240
|
+
this.msalConfig.auth.clientSecret = this.clientSecret;
|
|
3241
|
+
}
|
|
3242
|
+
return super.init(options);
|
|
3243
|
+
}
|
|
3244
|
+
async doGetToken(scopes, options = {}) {
|
|
3245
|
+
try {
|
|
3246
|
+
const result = await this.confidentialApp.acquireTokenOnBehalfOf({
|
|
3247
|
+
scopes,
|
|
3248
|
+
correlationId: options.correlationId,
|
|
3249
|
+
authority: options.authority,
|
|
3250
|
+
oboAssertion: this.userAssertionToken
|
|
3251
|
+
});
|
|
3252
|
+
return this.handleResult(scopes, this.clientId, result || undefined);
|
|
3253
|
+
}
|
|
3254
|
+
catch (err) {
|
|
3255
|
+
throw this.handleError(scopes, err, options);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
// Copyright (c) Microsoft Corporation.
|
|
3261
|
+
const credentialName = "OnBehalfOfCredential";
|
|
3262
|
+
const logger$i = credentialLogger(credentialName);
|
|
3263
|
+
/**
|
|
3264
|
+
* Enables authentication to Azure Active Directory using the [On Behalf Of flow](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow).
|
|
3265
|
+
*/
|
|
3266
|
+
class OnBehalfOfCredential {
|
|
3267
|
+
/**
|
|
3268
|
+
* Creates an instance of the {@link OnBehalfOfCredential} with the details
|
|
3269
|
+
* needed to authenticate against Azure Active Directory with a client
|
|
3270
|
+
* secret or a path to a PEM certificate, and an user assertion.
|
|
3271
|
+
*
|
|
3272
|
+
* Example using the `KeyClient` from [\@azure/keyvault-keys](https://www.npmjs.com/package/\@azure/keyvault-keys):
|
|
3273
|
+
*
|
|
3274
|
+
* ```ts
|
|
3275
|
+
* const tokenCredential = new OnBehalfOfCredential({
|
|
3276
|
+
* tenantId,
|
|
3277
|
+
* clientId,
|
|
3278
|
+
* clientSecret, // or `certificatePath: "/path/to/certificate.pem"
|
|
3279
|
+
* userAssertionToken: "access-token"
|
|
3280
|
+
* });
|
|
3281
|
+
* const client = new KeyClient("vault-url", tokenCredential);
|
|
3282
|
+
*
|
|
3283
|
+
* await client.getKey("key-name");
|
|
3284
|
+
* ```
|
|
3285
|
+
*
|
|
3286
|
+
* @param configuration - Configuration specific to this credential.
|
|
3287
|
+
* @param options - Optional parameters, generally common across credentials.
|
|
3288
|
+
*/
|
|
3289
|
+
constructor(configuration, options = {}) {
|
|
3290
|
+
this.configuration = configuration;
|
|
3291
|
+
this.options = options;
|
|
3292
|
+
const { tenantId, clientId, userAssertionToken } = configuration;
|
|
3293
|
+
const secretConfiguration = configuration;
|
|
3294
|
+
const certificateConfiguration = configuration;
|
|
3295
|
+
if (!tenantId ||
|
|
3296
|
+
!clientId ||
|
|
3297
|
+
!(secretConfiguration.clientSecret || certificateConfiguration.certificatePath) ||
|
|
3298
|
+
!userAssertionToken) {
|
|
3299
|
+
throw new Error(`${credentialName}: tenantId, clientId, clientSecret (or certificatePath) and userAssertionToken are required parameters.`);
|
|
3300
|
+
}
|
|
3301
|
+
this.msalFlow = new MsalOnBehalfOf(Object.assign(Object.assign(Object.assign({}, this.options), this.configuration), { logger: logger$i, tokenCredentialOptions: this.options }));
|
|
3302
|
+
}
|
|
3303
|
+
/**
|
|
3304
|
+
* Authenticates with Azure Active Directory and returns an access token if successful.
|
|
3305
|
+
* If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure.
|
|
3306
|
+
*
|
|
3307
|
+
* @param scopes - The list of scopes for which the token will have access.
|
|
3308
|
+
* @param options - The options used to configure the underlying network requests.
|
|
3309
|
+
*/
|
|
3310
|
+
async getToken(scopes, options = {}) {
|
|
3311
|
+
return trace(`${credentialName}.getToken`, options, async (newOptions) => {
|
|
3312
|
+
const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
|
|
3313
|
+
return this.msalFlow.getToken(arrayScopes, newOptions);
|
|
3314
|
+
});
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3069
3318
|
// Copyright (c) Microsoft Corporation.
|
|
3070
3319
|
/**
|
|
3071
3320
|
* Returns a new instance of the {@link DefaultAzureCredential}.
|
|
@@ -3093,11 +3342,12 @@ exports.DeviceCodeCredential = DeviceCodeCredential;
|
|
|
3093
3342
|
exports.EnvironmentCredential = EnvironmentCredential;
|
|
3094
3343
|
exports.InteractiveBrowserCredential = InteractiveBrowserCredential;
|
|
3095
3344
|
exports.ManagedIdentityCredential = ManagedIdentityCredential;
|
|
3345
|
+
exports.OnBehalfOfCredential = OnBehalfOfCredential;
|
|
3096
3346
|
exports.UsernamePasswordCredential = UsernamePasswordCredential;
|
|
3097
3347
|
exports.VisualStudioCodeCredential = VisualStudioCodeCredential;
|
|
3098
3348
|
exports.deserializeAuthenticationRecord = deserializeAuthenticationRecord;
|
|
3099
3349
|
exports.getDefaultAzureCredential = getDefaultAzureCredential;
|
|
3100
3350
|
exports.logger = logger;
|
|
3101
3351
|
exports.serializeAuthenticationRecord = serializeAuthenticationRecord;
|
|
3102
|
-
exports.
|
|
3352
|
+
exports.useIdentityPlugin = useIdentityPlugin;
|
|
3103
3353
|
//# sourceMappingURL=index.js.map
|