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