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