'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var logger$r = require('@azure/logger'); var coreClient = require('@azure/core-client'); var coreUtil = require('@azure/core-util'); var coreRestPipeline = require('@azure/core-rest-pipeline'); var abortController = require('@azure/abort-controller'); var coreTracing = require('@azure/core-tracing'); var fs = require('fs'); var os = require('os'); var path = require('path'); var msalCommon = require('@azure/msal-node'); var fs$1 = require('node:fs'); var https = require('https'); var promises = require('fs/promises'); var child_process = require('child_process'); var crypto = require('crypto'); var open = require('open'); var util = require('util'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var msalCommon__namespace = /*#__PURE__*/_interopNamespaceDefault(msalCommon); var child_process__namespace = /*#__PURE__*/_interopNamespaceDefault(child_process); // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Current version of the `@azure/identity` package. */ const SDK_VERSION = `4.3.0-beta.3`; /** * The default client ID for authentication * @internal */ // TODO: temporary - this is the Azure CLI clientID - we'll replace it when // Developer Sign On application is available // https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/Constants.cs#L9 const DeveloperSignOnClientId = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"; /** * The default tenant for authentication * @internal */ const DefaultTenantId = "common"; /** * A list of known Azure authority hosts */ exports.AzureAuthorityHosts = void 0; (function (AzureAuthorityHosts) { /** * China-based Azure Authority Host */ AzureAuthorityHosts["AzureChina"] = "https://login.chinacloudapi.cn"; /** * Germany-based Azure Authority Host */ AzureAuthorityHosts["AzureGermany"] = "https://login.microsoftonline.de"; /** * US Government Azure Authority Host */ AzureAuthorityHosts["AzureGovernment"] = "https://login.microsoftonline.us"; /** * Public Cloud Azure Authority Host */ AzureAuthorityHosts["AzurePublicCloud"] = "https://login.microsoftonline.com"; })(exports.AzureAuthorityHosts || (exports.AzureAuthorityHosts = {})); /** * @internal * The default authority host. */ const DefaultAuthorityHost = exports.AzureAuthorityHosts.AzurePublicCloud; /** * @internal * Allow acquiring tokens for any tenant for multi-tentant auth. */ const ALL_TENANTS = ["*"]; /** * @internal */ const CACHE_CAE_SUFFIX = "cae"; /** * @internal */ const CACHE_NON_CAE_SUFFIX = "nocae"; /** * @internal * * The default name for the cache persistence plugin. * Matches the constant defined in the cache persistence package. */ const DEFAULT_TOKEN_CACHE_NAME = "msal.cache"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * The current persistence provider, undefined by default. * @internal */ let persistenceProvider = undefined; /** * An object that allows setting the persistence provider. * @internal */ const msalNodeFlowCacheControl = { setPersistence(pluginProvider) { persistenceProvider = pluginProvider; }, }; /** * The current native broker provider, undefined by default. * @internal */ let nativeBrokerInfo = undefined; function hasNativeBroker() { return nativeBrokerInfo !== undefined; } /** * An object that allows setting the native broker provider. * @internal */ const msalNodeFlowNativeBrokerControl = { setNativeBroker(broker) { nativeBrokerInfo = { broker, }; }, }; /** * Configures plugins, validating that required plugins are available and enabled. * * Does not create the plugins themselves, but rather returns the configuration that will be used to create them. * * @param options - options for creating the MSAL client * @returns plugin configuration */ function generatePluginConfiguration(options) { var _a, _b, _c, _d, _e, _f, _g; const config = { cache: {}, broker: { isEnabled: (_b = (_a = options.brokerOptions) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : false, enableMsaPassthrough: (_d = (_c = options.brokerOptions) === null || _c === void 0 ? void 0 : _c.legacyEnableMsaPassthrough) !== null && _d !== void 0 ? _d : false, parentWindowHandle: (_e = options.brokerOptions) === null || _e === void 0 ? void 0 : _e.parentWindowHandle, }, }; if ((_f = options.tokenCachePersistenceOptions) === null || _f === void 0 ? void 0 : _f.enabled) { if (persistenceProvider === undefined) { throw new Error([ "Persistent token caching was requested, but no persistence provider was configured.", "You must install the identity-cache-persistence plugin package (`npm install --save @azure/identity-cache-persistence`)", "and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling", "`useIdentityPlugin(cachePersistencePlugin)` before using `tokenCachePersistenceOptions`.", ].join(" ")); } const cacheBaseName = options.tokenCachePersistenceOptions.name || DEFAULT_TOKEN_CACHE_NAME; config.cache.cachePlugin = persistenceProvider(Object.assign({ name: `${cacheBaseName}.${CACHE_NON_CAE_SUFFIX}` }, options.tokenCachePersistenceOptions)); config.cache.cachePluginCae = persistenceProvider(Object.assign({ name: `${cacheBaseName}.${CACHE_CAE_SUFFIX}` }, options.tokenCachePersistenceOptions)); } if ((_g = options.brokerOptions) === null || _g === void 0 ? void 0 : _g.enabled) { if (nativeBrokerInfo === undefined) { throw new Error([ "Broker for WAM was requested to be enabled, but no native broker was configured.", "You must install the identity-broker plugin package (`npm install --save @azure/identity-broker`)", "and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling", "`useIdentityPlugin(createNativeBrokerPlugin())` before using `enableBroker`.", ].join(" ")); } config.broker.nativeBrokerPlugin = nativeBrokerInfo.broker; } return config; } /** * Wraps generatePluginConfiguration as a writeable property for test stubbing purposes. */ const msalPlugins = { generatePluginConfiguration, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * The AzureLogger used for all clients within the identity package */ const logger$q = logger$r.createClientLogger("identity"); /** * 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. * @param supportedEnvVars - List of environment variable names */ function processEnvVars(supportedEnvVars) { return supportedEnvVars.reduce((acc, envVariable) => { if (process.env[envVariable]) { acc.assigned.push(envVariable); } else { acc.missing.push(envVariable); } return acc; }, { missing: [], assigned: [] }); } /** * Formatting the success event on the credentials */ function formatSuccess(scope) { return `SUCCESS. Scopes: ${Array.isArray(scope) ? scope.join(", ") : scope}.`; } /** * Formatting the success event on the credentials */ function formatError(scope, error) { let message = "ERROR."; if (scope === null || scope === void 0 ? void 0 : scope.length) { message += ` Scopes: ${Array.isArray(scope) ? scope.join(", ") : scope}.`; } return `${message} Error message: ${typeof error === "string" ? error : error.message}.`; } /** * Generates a CredentialLoggerInstance. * * It logs with the format: * * `[title] => [message]` * */ function credentialLoggerInstance(title, parent, log = logger$q) { const fullTitle = parent ? `${parent.fullTitle} ${title}` : title; function info(message) { log.info(`${fullTitle} =>`, message); } function warning(message) { log.warning(`${fullTitle} =>`, message); } function verbose(message) { log.verbose(`${fullTitle} =>`, message); } function error(message) { log.error(`${fullTitle} =>`, message); } return { title, fullTitle, info, warning, verbose, error, }; } /** * Generates a CredentialLogger, which is a logger declared at the credential's constructor, and used at any point in the credential. * It has all the properties of a CredentialLoggerInstance, plus other logger instances, one per method. * * It logs with the format: * * `[title] => [message]` * `[title] => getToken() => [message]` * */ function credentialLogger(title, log = logger$q) { const credLogger = credentialLoggerInstance(title, undefined, log); return Object.assign(Object.assign({}, credLogger), { parent: log, getToken: credentialLoggerInstance("=> getToken()", credLogger, log) }); } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. function isErrorResponse(errorResponse) { return (errorResponse && typeof errorResponse.error === "string" && typeof errorResponse.error_description === "string"); } /** * The Error.name value of an CredentialUnavailable */ const CredentialUnavailableErrorName = "CredentialUnavailableError"; /** * This signifies that the credential that was tried in a chained credential * was not available to be used as the credential. Rather than treating this as * an error that should halt the chain, it's caught and the chain continues */ class CredentialUnavailableError extends Error { constructor(message) { super(message); this.name = CredentialUnavailableErrorName; } } /** * The Error.name value of an AuthenticationError */ const AuthenticationErrorName = "AuthenticationError"; /** * Provides details about a failure to authenticate with Azure Active * Directory. The `errorResponse` field contains more details about * the specific failure. */ class AuthenticationError extends Error { // eslint-disable-next-line @typescript-eslint/ban-types constructor(statusCode, errorBody) { let errorResponse = { error: "unknown", errorDescription: "An unknown error occurred and no additional details are available.", }; if (isErrorResponse(errorBody)) { errorResponse = convertOAuthErrorResponseToErrorResponse(errorBody); } else if (typeof errorBody === "string") { try { // Most error responses will contain JSON-formatted error details // in the response body const oauthErrorResponse = JSON.parse(errorBody); errorResponse = convertOAuthErrorResponseToErrorResponse(oauthErrorResponse); } catch (e) { if (statusCode === 400) { errorResponse = { error: "authority_not_found", errorDescription: "The specified authority URL was not found.", }; } else { errorResponse = { error: "unknown_error", errorDescription: `An unknown error has occurred. Response body:\n\n${errorBody}`, }; } } } else { errorResponse = { error: "unknown_error", errorDescription: "An unknown error occurred and no additional details are available.", }; } super(`${errorResponse.error} Status code: ${statusCode}\nMore details:\n${errorResponse.errorDescription}`); this.statusCode = statusCode; this.errorResponse = errorResponse; // Ensure that this type reports the correct name this.name = AuthenticationErrorName; } } /** * The Error.name value of an AggregateAuthenticationError */ const AggregateAuthenticationErrorName = "AggregateAuthenticationError"; /** * Provides an `errors` array containing {@link AuthenticationError} instance * for authentication failures from credentials in a {@link ChainedTokenCredential}. */ class AggregateAuthenticationError extends Error { constructor(errors, errorMessage) { const errorDetail = errors.join("\n"); super(`${errorMessage}\n${errorDetail}`); this.errors = errors; // Ensure that this type reports the correct name this.name = AggregateAuthenticationErrorName; } } function convertOAuthErrorResponseToErrorResponse(errorBody) { return { error: errorBody.error, errorDescription: errorBody.error_description, correlationId: errorBody.correlation_id, errorCodes: errorBody.error_codes, timestamp: errorBody.timestamp, traceId: errorBody.trace_id, }; } /** * Error used to enforce authentication after trying to retrieve a token silently. */ class AuthenticationRequiredError extends Error { constructor( /** * 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. */ options) { super(options.message); this.scopes = options.scopes; this.getTokenOptions = options.getTokenOptions; this.name = "AuthenticationRequiredError"; } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. function createConfigurationErrorMessage(tenantId) { return `The current credential is not configured to acquire tokens for tenant ${tenantId}. To enable acquiring tokens for this tenant add it to the AdditionallyAllowedTenants on the credential options, or add "*" to AdditionallyAllowedTenants to allow acquiring tokens for any tenant.`; } /** * Of getToken contains a tenantId, this functions allows picking this tenantId as the appropriate for authentication, * unless multitenant authentication has been disabled through the AZURE_IDENTITY_DISABLE_MULTITENANTAUTH (on Node.js), * or unless the original tenant Id is `adfs`. * @internal */ function processMultiTenantRequest(tenantId, getTokenOptions, additionallyAllowedTenantIds = [], logger) { var _a; let resolvedTenantId; if (process.env.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH) { resolvedTenantId = tenantId; } else if (tenantId === "adfs") { resolvedTenantId = tenantId; } else { resolvedTenantId = (_a = getTokenOptions === null || getTokenOptions === void 0 ? void 0 : getTokenOptions.tenantId) !== null && _a !== void 0 ? _a : tenantId; } if (tenantId && resolvedTenantId !== tenantId && !additionallyAllowedTenantIds.includes("*") && !additionallyAllowedTenantIds.some((t) => t.localeCompare(resolvedTenantId) === 0)) { const message = createConfigurationErrorMessage(tenantId); logger === null || logger === void 0 ? void 0 : logger.info(message); throw new CredentialUnavailableError(message); } return resolvedTenantId; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * @internal */ function checkTenantId(logger, tenantId) { if (!tenantId.match(/^[0-9a-zA-Z-.]+$/)) { const error = new Error("Invalid tenant id provided. You can locate your tenant id by following the instructions listed here: https://learn.microsoft.com/partner-center/find-ids-and-domain-names."); logger.info(formatError("", error)); throw error; } } /** * @internal */ function resolveTenantId(logger, tenantId, clientId) { if (tenantId) { checkTenantId(logger, tenantId); return tenantId; } if (!clientId) { clientId = DeveloperSignOnClientId; } if (clientId !== DeveloperSignOnClientId) { return "common"; } return "organizations"; } /** * @internal */ function resolveAdditionallyAllowedTenantIds(additionallyAllowedTenants) { if (!additionallyAllowedTenants || additionallyAllowedTenants.length === 0) { return []; } if (additionallyAllowedTenants.includes("*")) { return ALL_TENANTS; } return additionallyAllowedTenants; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. function getIdentityTokenEndpointSuffix(tenantId) { if (tenantId === "adfs") { return "oauth2/token"; } else { return "oauth2/v2.0/token"; } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Creates a span using the global tracer. * @internal */ const tracingClient = coreTracing.createTracingClient({ namespace: "Microsoft.AAD", packageName: "@azure/identity", packageVersion: SDK_VERSION, }); // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const DefaultScopeSuffix = "/.default"; const imdsHost = "http://169.254.169.254"; const imdsEndpointPath = "/metadata/identity/oauth2/token"; const imdsApiVersion = "2018-02-01"; const azureArcAPIVersion = "2019-11-01"; const azureFabricVersion = "2019-07-01-preview"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * 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. * This resource can be derived from the scopes received through the getToken call, as long as only one scope is received. * Multiple scopes assume that the resulting token will have access to multiple resources, which won't be the case. * * For that reason, when we encounter multiple scopes, we return undefined. * It's up to the individual MSI implementations to throw the errors (which helps us provide less generic errors). */ function mapScopesToResource(scopes) { let scope = ""; if (Array.isArray(scopes)) { if (scopes.length !== 1) { return; } scope = scopes[0]; } else if (typeof scopes === "string") { scope = scopes; } if (!scope.endsWith(DefaultScopeSuffix)) { return scope; } return scope.substr(0, scope.lastIndexOf(DefaultScopeSuffix)); } /** * Given a token response, return the expiration timestamp as the number of milliseconds from the Unix epoch. * @param body - A parsed response body from the authentication endpoint. */ function parseExpirationTimestamp(body) { if (typeof body.expires_on === "number") { return body.expires_on * 1000; } if (typeof body.expires_on === "string") { const asNumber = +body.expires_on; if (!isNaN(asNumber)) { return asNumber * 1000; } const asDate = Date.parse(body.expires_on); if (!isNaN(asDate)) { return asDate; } } if (typeof body.expires_in === "number") { return Date.now() + body.expires_in * 1000; } throw new Error(`Failed to parse token expiration from body. expires_in="${body.expires_in}", expires_on="${body.expires_on}"`); } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const noCorrelationId = "noCorrelationId"; /** * @internal */ function getIdentityClientAuthorityHost(options) { // The authorityHost can come from options or from the AZURE_AUTHORITY_HOST environment variable. let authorityHost = options === null || options === void 0 ? void 0 : options.authorityHost; // The AZURE_AUTHORITY_HOST environment variable can only be provided in Node.js. if (coreUtil.isNode) { authorityHost = authorityHost !== null && authorityHost !== void 0 ? authorityHost : process.env.AZURE_AUTHORITY_HOST; } // If the authorityHost is not provided, we use the default one from the public cloud: https://login.microsoftonline.com return authorityHost !== null && authorityHost !== void 0 ? authorityHost : DefaultAuthorityHost; } /** * The network module used by the Identity credentials. * * It allows for credentials to abort any pending request independently of the MSAL flow, * by calling to the `abortRequests()` method. * */ class IdentityClient extends coreClient.ServiceClient { constructor(options) { var _a, _b; const packageDetails = `azsdk-js-identity/${SDK_VERSION}`; const userAgentPrefix = ((_a = options === null || options === void 0 ? void 0 : options.userAgentOptions) === null || _a === void 0 ? void 0 : _a.userAgentPrefix) ? `${options.userAgentOptions.userAgentPrefix} ${packageDetails}` : `${packageDetails}`; const baseUri = getIdentityClientAuthorityHost(options); if (!baseUri.startsWith("https:")) { throw new Error("The authorityHost address must use the 'https' protocol."); } super(Object.assign(Object.assign({ requestContentType: "application/json; charset=utf-8", retryOptions: { maxRetries: 3, } }, options), { userAgentOptions: { userAgentPrefix, }, baseUri })); this.authorityHost = baseUri; this.abortControllers = new Map(); this.allowLoggingAccountIdentifiers = (_b = options === null || options === void 0 ? void 0 : options.loggingOptions) === null || _b === void 0 ? void 0 : _b.allowLoggingAccountIdentifiers; // used for WorkloadIdentity this.tokenCredentialOptions = Object.assign({}, options); } async sendTokenRequest(request) { logger$q.info(`IdentityClient: sending token request to [${request.url}]`); const response = await this.sendRequest(request); if (response.bodyAsText && (response.status === 200 || response.status === 201)) { const parsedBody = JSON.parse(response.bodyAsText); if (!parsedBody.access_token) { return null; } this.logIdentifiers(response); const token = { accessToken: { token: parsedBody.access_token, expiresOnTimestamp: parseExpirationTimestamp(parsedBody), }, refreshToken: parsedBody.refresh_token, }; logger$q.info(`IdentityClient: [${request.url}] token acquired, expires on ${token.accessToken.expiresOnTimestamp}`); return token; } else { const error = new AuthenticationError(response.status, response.bodyAsText); logger$q.warning(`IdentityClient: authentication error. HTTP status: ${response.status}, ${error.errorResponse.errorDescription}`); throw error; } } async refreshAccessToken(tenantId, clientId, scopes, refreshToken, clientSecret, options = {}) { if (refreshToken === undefined) { return null; } logger$q.info(`IdentityClient: refreshing access token with client ID: ${clientId}, scopes: ${scopes} started`); const refreshParams = { grant_type: "refresh_token", client_id: clientId, refresh_token: refreshToken, scope: scopes, }; if (clientSecret !== undefined) { refreshParams.client_secret = clientSecret; } const query = new URLSearchParams(refreshParams); return tracingClient.withSpan("IdentityClient.refreshAccessToken", options, async (updatedOptions) => { try { const urlSuffix = getIdentityTokenEndpointSuffix(tenantId); const request = coreRestPipeline.createPipelineRequest({ url: `${this.authorityHost}/${tenantId}/${urlSuffix}`, method: "POST", body: query.toString(), abortSignal: options.abortSignal, headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", }), tracingOptions: updatedOptions.tracingOptions, }); const response = await this.sendTokenRequest(request); logger$q.info(`IdentityClient: refreshed token for client ID: ${clientId}`); return response; } catch (err) { if (err.name === AuthenticationErrorName && err.errorResponse.error === "interaction_required") { // It's likely that the refresh token has expired, so // return null so that the credential implementation will // initiate the authentication flow again. logger$q.info(`IdentityClient: interaction required for client ID: ${clientId}`); return null; } else { logger$q.warning(`IdentityClient: failed refreshing token for client ID: ${clientId}: ${err}`); throw err; } } }); } // Here is a custom layer that allows us to abort requests that go through MSAL, // since MSAL doesn't allow us to pass options all the way through. generateAbortSignal(correlationId) { const controller = new abortController.AbortController(); const controllers = this.abortControllers.get(correlationId) || []; controllers.push(controller); this.abortControllers.set(correlationId, controllers); const existingOnAbort = controller.signal.onabort; controller.signal.onabort = (...params) => { this.abortControllers.set(correlationId, undefined); if (existingOnAbort) { existingOnAbort(...params); } }; return controller.signal; } abortRequests(correlationId) { const key = correlationId || noCorrelationId; const controllers = [ ...(this.abortControllers.get(key) || []), // MSAL passes no correlation ID to the get requests... ...(this.abortControllers.get(noCorrelationId) || []), ]; if (!controllers.length) { return; } for (const controller of controllers) { controller.abort(); } this.abortControllers.set(key, undefined); } getCorrelationId(options) { var _a; 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"); return parameter && parameter.length ? parameter[1] || noCorrelationId : noCorrelationId; } // The MSAL network module methods follow async sendGetRequestAsync(url, options) { const request = coreRestPipeline.createPipelineRequest({ url, method: "GET", body: options === null || options === void 0 ? void 0 : options.body, headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers), abortSignal: this.generateAbortSignal(noCorrelationId), }); const response = await this.sendRequest(request); this.logIdentifiers(response); return { body: response.bodyAsText ? JSON.parse(response.bodyAsText) : undefined, headers: response.headers.toJSON(), status: response.status, }; } async sendPostRequestAsync(url, options) { const request = coreRestPipeline.createPipelineRequest({ url, method: "POST", body: options === null || options === void 0 ? void 0 : options.body, headers: coreRestPipeline.createHttpHeaders(options === null || options === void 0 ? void 0 : options.headers), // MSAL doesn't send the correlation ID on the get requests. abortSignal: this.generateAbortSignal(this.getCorrelationId(options)), }); const response = await this.sendRequest(request); this.logIdentifiers(response); return { body: response.bodyAsText ? JSON.parse(response.bodyAsText) : undefined, headers: response.headers.toJSON(), status: response.status, }; } /** * * @internal */ getTokenCredentialOptions() { return this.tokenCredentialOptions; } /** * If allowLoggingAccountIdentifiers was set on the constructor options * we try to log the account identifiers by parsing the received access token. * * The account identifiers we try to log are: * - `appid`: The application or Client Identifier. * - `upn`: User Principal Name. * - It might not be available in some authentication scenarios. * - If it's not available, we put a placeholder: "No User Principal Name available". * - `tid`: Tenant Identifier. * - `oid`: Object Identifier of the authenticated user. */ logIdentifiers(response) { if (!this.allowLoggingAccountIdentifiers || !response.bodyAsText) { return; } const unavailableUpn = "No User Principal Name available"; try { const parsed = response.parsedBody || JSON.parse(response.bodyAsText); const accessToken = parsed.access_token; if (!accessToken) { // Without an access token allowLoggingAccountIdentifiers isn't useful. return; } const base64Metadata = accessToken.split(".")[1]; const { appid, upn, tid, oid } = JSON.parse(Buffer.from(base64Metadata, "base64").toString("utf8")); logger$q.info(`[Authenticated account] Client ID: ${appid}. Tenant ID: ${tid}. User Principal Name: ${upn || unavailableUpn}. Object ID (user): ${oid}`); } catch (e) { logger$q.warning("allowLoggingAccountIdentifiers was set, but we couldn't log the account information. Error:", e.message); } } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const CommonTenantId = "common"; const AzureAccountClientId = "aebc6443-996d-45c2-90f0-388ff96faa56"; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56' const logger$p = credentialLogger("VisualStudioCodeCredential"); let findCredentials = undefined; const vsCodeCredentialControl = { setVsCodeCredentialFinder(finder) { findCredentials = finder; }, }; // Map of unsupported Tenant IDs and the errors we will be throwing. const unsupportedTenantIds = { adfs: "The VisualStudioCodeCredential does not support authentication with ADFS tenants.", }; function checkUnsupportedTenant(tenantId) { // If the Tenant ID isn't supported, we throw. const unsupportedTenantError = unsupportedTenantIds[tenantId]; if (unsupportedTenantError) { throw new CredentialUnavailableError(unsupportedTenantError); } } const mapVSCodeAuthorityHosts = { AzureCloud: exports.AzureAuthorityHosts.AzurePublicCloud, AzureChina: exports.AzureAuthorityHosts.AzureChina, AzureGermanCloud: exports.AzureAuthorityHosts.AzureGermany, AzureUSGovernment: exports.AzureAuthorityHosts.AzureGovernment, }; /** * Attempts to load a specific property from the VSCode configurations of the current OS. * If it fails at any point, returns undefined. */ function getPropertyFromVSCode(property) { const settingsPath = ["User", "settings.json"]; // Eventually we can add more folders for more versions of VSCode. const vsCodeFolder = "Code"; const homedir = os.homedir(); function loadProperty(...pathSegments) { const fullPath = path.join(...pathSegments, vsCodeFolder, ...settingsPath); const settings = JSON.parse(fs.readFileSync(fullPath, { encoding: "utf8" })); return settings[property]; } try { let appData; switch (process.platform) { case "win32": appData = process.env.APPDATA; return appData ? loadProperty(appData) : undefined; case "darwin": return loadProperty(homedir, "Library", "Application Support"); case "linux": return loadProperty(homedir, ".config"); default: return; } } catch (e) { logger$p.info(`Failed to load the Visual Studio Code configuration file. Error: ${e.message}`); return; } } /** * Connects to Azure using the credential provided by the VSCode extension 'Azure Account'. * Once the user has logged in via the extension, this credential can share the same refresh token * that is cached by the extension. * * It's a [known issue](https://github.com/Azure/azure-sdk-for-js/issues/20500) that this credential doesn't * work with [Azure Account extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.azure-account) * versions newer than **0.9.11**. A long-term fix to this problem is in progress. In the meantime, consider * authenticating with {@link AzureCliCredential}. */ class VisualStudioCodeCredential { /** * Creates an instance of VisualStudioCodeCredential to use for automatically authenticating via VSCode. * * **Note**: `VisualStudioCodeCredential` is provided by a plugin package: * `@azure/identity-vscode`. If this package is not installed and registered * using the plugin API (`useIdentityPlugin`), then authentication using * `VisualStudioCodeCredential` will not be available. * * @param options - Options for configuring the client which makes the authentication request. */ constructor(options) { // We want to make sure we use the one assigned by the user on the VSCode settings. // Or just `AzureCloud` by default. this.cloudName = (getPropertyFromVSCode("azure.cloud") || "AzureCloud"); // Picking an authority host based on the cloud name. const authorityHost = mapVSCodeAuthorityHosts[this.cloudName]; this.identityClient = new IdentityClient(Object.assign({ authorityHost }, options)); if (options && options.tenantId) { checkTenantId(logger$p, options.tenantId); this.tenantId = options.tenantId; } else { this.tenantId = CommonTenantId; } this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants); checkUnsupportedTenant(this.tenantId); } /** * Runs preparations for any further getToken request. */ async prepare() { // Attempts to load the tenant from the VSCode configuration file. const settingsTenant = getPropertyFromVSCode("azure.tenant"); if (settingsTenant) { this.tenantId = settingsTenant; } checkUnsupportedTenant(this.tenantId); } /** * Runs preparations for any further getToken, but only once. */ prepareOnce() { if (!this.preparePromise) { this.preparePromise = this.prepare(); } return this.preparePromise; } /** * Returns the token found by searching VSCode's authentication cache or * returns null if no token could be found. * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * `TokenCredential` implementation might make. */ async getToken(scopes, options) { var _a, _b; await this.prepareOnce(); const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds, logger$p) || this.tenantId; if (findCredentials === undefined) { throw new CredentialUnavailableError([ "No implementation of `VisualStudioCodeCredential` is available.", "You must install the identity-vscode plugin package (`npm install --save-dev @azure/identity-vscode`)", "and enable it by importing `useIdentityPlugin` from `@azure/identity` and calling", "`useIdentityPlugin(vsCodePlugin)` before creating a `VisualStudioCodeCredential`.", "To troubleshoot, visit https://aka.ms/azsdk/js/identity/vscodecredential/troubleshoot.", ].join(" ")); } let scopeString = typeof scopes === "string" ? scopes : scopes.join(" "); // Check to make sure the scope we get back is a valid scope if (!scopeString.match(/^[0-9a-zA-Z-.:/]+$/)) { const error = new Error("Invalid scope was specified by the user or calling client"); logger$p.getToken.info(formatError(scopes, error)); throw error; } if (scopeString.indexOf("offline_access") < 0) { scopeString += " offline_access"; } // findCredentials returns an array similar to: // [ // { // account: "", // password: "", // }, // /* ... */ // ] const credentials = await findCredentials(); // If we can't find the credential based on the name, we'll pick the first one available. const { password: refreshToken } = (_b = (_a = credentials.find(({ account }) => account === this.cloudName)) !== null && _a !== void 0 ? _a : credentials[0]) !== null && _b !== void 0 ? _b : {}; if (refreshToken) { const tokenResponse = await this.identityClient.refreshAccessToken(tenantId, AzureAccountClientId, scopeString, refreshToken, undefined); if (tokenResponse) { logger$p.getToken.info(formatSuccess(scopes)); return tokenResponse.accessToken; } else { const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Have you connected using the 'Azure Account' extension recently? To troubleshoot, visit https://aka.ms/azsdk/js/identity/vscodecredential/troubleshoot."); logger$p.getToken.info(formatError(scopes, error)); throw error; } } else { const error = new CredentialUnavailableError("Could not retrieve the token associated with Visual Studio Code. Did you connect using the 'Azure Account' extension? To troubleshoot, visit https://aka.ms/azsdk/js/identity/vscodecredential/troubleshoot."); logger$p.getToken.info(formatError(scopes, error)); throw error; } } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * The context passed to an Identity plugin. This contains objects that * plugins can use to set backend implementations. * @internal */ const pluginContext = { cachePluginControl: msalNodeFlowCacheControl, nativeBrokerPluginControl: msalNodeFlowNativeBrokerControl, vsCodeCredentialControl: vsCodeCredentialControl, }; /** * Extend Azure Identity with additional functionality. Pass a plugin from * a plugin package, such as: * * - `@azure/identity-cache-persistence`: provides persistent token caching * - `@azure/identity-vscode`: provides the dependencies of * `VisualStudioCodeCredential` and enables it * * Example: * * ```javascript * import { cachePersistencePlugin } from "@azure/identity-cache-persistence"; * * import { useIdentityPlugin, DefaultAzureCredential } from "@azure/identity"; * useIdentityPlugin(cachePersistencePlugin); * * // The plugin has the capability to extend `DefaultAzureCredential` and to * // add middleware to the underlying credentials, such as persistence. * const credential = new DefaultAzureCredential({ * tokenCachePersistenceOptions: { * enabled: true * } * }); * ``` * * @param plugin - the plugin to register */ function useIdentityPlugin(plugin) { plugin(pluginContext); } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName$6 = "ManagedIdentityCredential - AppServiceMSI 2017"; const logger$o = credentialLogger(msiName$6); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions$5(scopes, clientId) { const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$6}: Multiple scopes are not supported.`); } const queryParameters = { resource, "api-version": "2017-09-01", }; if (clientId) { queryParameters.clientid = clientId; } const query = new URLSearchParams(queryParameters); // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below. if (!process.env.MSI_ENDPOINT) { throw new Error(`${msiName$6}: Missing environment variable: MSI_ENDPOINT`); } if (!process.env.MSI_SECRET) { throw new Error(`${msiName$6}: Missing environment variable: MSI_SECRET`); } return { url: `${process.env.MSI_ENDPOINT}?${query.toString()}`, method: "GET", headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", secret: process.env.MSI_SECRET, }), }; } /** * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI. */ const appServiceMsi2017 = { name: "appServiceMsi2017", async isAvailable({ scopes }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$o.info(`${msiName$6}: Unavailable. Multiple scopes are not supported.`); return false; } const env = process.env; const result = Boolean(env.MSI_ENDPOINT && env.MSI_SECRET); if (!result) { logger$o.info(`${msiName$6}: Unavailable. The environment variables needed are: MSI_ENDPOINT and MSI_SECRET.`); } return result; }, async getToken(configuration, getTokenOptions = {}) { const { identityClient, scopes, clientId, resourceId } = configuration; if (resourceId) { logger$o.warning(`${msiName$6}: managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`); } logger$o.info(`${msiName$6}: Using the endpoint and the secret coming form the environment variables: MSI_ENDPOINT=${process.env.MSI_ENDPOINT} and MSI_SECRET=[REDACTED].`); const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$5(scopes, clientId)), { // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS). allowInsecureConnection: true })); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName$5 = "ManagedIdentityCredential - AppServiceMSI 2019"; const logger$n = credentialLogger(msiName$5); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions$4(scopes, clientId, resourceId) { const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$5}: Multiple scopes are not supported.`); } const queryParameters = { resource, "api-version": "2019-08-01", }; if (clientId) { queryParameters.client_id = clientId; } if (resourceId) { queryParameters.mi_res_id = resourceId; } const query = new URLSearchParams(queryParameters); // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below. if (!process.env.IDENTITY_ENDPOINT) { throw new Error(`${msiName$5}: Missing environment variable: IDENTITY_ENDPOINT`); } if (!process.env.IDENTITY_HEADER) { throw new Error(`${msiName$5}: Missing environment variable: IDENTITY_HEADER`); } return { url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`, method: "GET", headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", "X-IDENTITY-HEADER": process.env.IDENTITY_HEADER, }), }; } /** * Defines how to determine whether the Azure App Service MSI is available, and also how to retrieve a token from the Azure App Service MSI. */ const appServiceMsi2019 = { name: "appServiceMsi2019", async isAvailable({ scopes }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$n.info(`${msiName$5}: Unavailable. Multiple scopes are not supported.`); return false; } const env = process.env; const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER); if (!result) { logger$n.info(`${msiName$5}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT and IDENTITY_HEADER.`); } return result; }, async getToken(configuration, getTokenOptions = {}) { const { identityClient, scopes, clientId, resourceId } = configuration; logger$n.info(`${msiName$5}: Using the endpoint and the secret coming form the environment variables: IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT} and IDENTITY_HEADER=[REDACTED].`); const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$4(scopes, clientId, resourceId)), { // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS). allowInsecureConnection: true })); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName$4 = "ManagedIdentityCredential - Azure Arc MSI"; const logger$m = credentialLogger(msiName$4); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions$3(scopes, clientId, resourceId) { const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$4}: Multiple scopes are not supported.`); } const queryParameters = { resource, "api-version": azureArcAPIVersion, }; if (clientId) { queryParameters.client_id = clientId; } if (resourceId) { queryParameters.msi_res_id = resourceId; } // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below. if (!process.env.IDENTITY_ENDPOINT) { throw new Error(`${msiName$4}: Missing environment variable: IDENTITY_ENDPOINT`); } const query = new URLSearchParams(queryParameters); return coreRestPipeline.createPipelineRequest({ // Should be similar to: http://localhost:40342/metadata/identity/oauth2/token url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`, method: "GET", headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", Metadata: "true", }), }); } /** * Does a request to the authentication provider that results in a file path. */ async function filePathRequest(identityClient, requestPrepareOptions) { const response = await identityClient.sendRequest(coreRestPipeline.createPipelineRequest(requestPrepareOptions)); if (response.status !== 401) { let message = ""; if (response.bodyAsText) { message = ` Response: ${response.bodyAsText}`; } throw new AuthenticationError(response.status, `${msiName$4}: To authenticate with Azure Arc MSI, status code 401 is expected on the first request. ${message}`); } const authHeader = response.headers.get("www-authenticate") || ""; try { return authHeader.split("=").slice(1)[0]; } catch (e) { throw Error(`Invalid www-authenticate header format: ${authHeader}`); } } function platformToFilePath() { switch (process.platform) { case "win32": if (!process.env.PROGRAMDATA) { throw new Error(`${msiName$4}: PROGRAMDATA environment variable has no value.`); } return `${process.env.PROGRAMDATA}\\AzureConnectedMachineAgent\\Tokens`; case "linux": return "/var/opt/azcmagent/tokens"; default: throw new Error(`${msiName$4}: Unsupported platform ${process.platform}.`); } } /** * Validates that a given Azure Arc MSI file path is valid for use. * * A valid file will: * 1. Be in the expected path for the current platform. * 2. Have a `.key` extension. * 3. Be at most 4096 bytes in size. */ function validateKeyFile(filePath) { if (!filePath) { throw new Error(`${msiName$4}: Failed to find the token file.`); } if (!filePath.endsWith(".key")) { throw new Error(`${msiName$4}: unexpected file path from HIMDS service: ${filePath}.`); } const expectedPath = platformToFilePath(); if (!filePath.startsWith(expectedPath)) { throw new Error(`${msiName$4}: unexpected file path from HIMDS service: ${filePath}.`); } const stats = fs$1.statSync(filePath); if (stats.size > 4096) { throw new Error(`${msiName$4}: The file at ${filePath} is larger than expected at ${stats.size} bytes.`); } } /** * Defines how to determine whether the Azure Arc MSI is available, and also how to retrieve a token from the Azure Arc MSI. */ const arcMsi = { name: "arc", async isAvailable({ scopes }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$m.info(`${msiName$4}: Unavailable. Multiple scopes are not supported.`); return false; } const result = Boolean(process.env.IMDS_ENDPOINT && process.env.IDENTITY_ENDPOINT); if (!result) { logger$m.info(`${msiName$4}: The environment variables needed are: IMDS_ENDPOINT and IDENTITY_ENDPOINT`); } return result; }, async getToken(configuration, getTokenOptions = {}) { var _a; const { identityClient, scopes, clientId, resourceId } = configuration; if (clientId) { logger$m.warning(`${msiName$4}: user-assigned identities not supported. The argument clientId might be ignored by the service.`); } if (resourceId) { logger$m.warning(`${msiName$4}: user defined managed Identity by resource Id is not supported. Argument resourceId will be ignored.`); } logger$m.info(`${msiName$4}: Authenticating.`); const requestOptions = Object.assign(Object.assign({ disableJsonStringifyOnBody: true, deserializationMapper: undefined, abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$3(scopes, clientId, resourceId)), { allowInsecureConnection: true }); const filePath = await filePathRequest(identityClient, requestOptions); validateKeyFile(filePath); const key = await fs$1.promises.readFile(filePath, { encoding: "utf-8" }); (_a = requestOptions.headers) === null || _a === void 0 ? void 0 : _a.set("Authorization", `Basic ${key}`); const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({}, requestOptions), { // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS). allowInsecureConnection: true })); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName$3 = "ManagedIdentityCredential - CloudShellMSI"; const logger$l = credentialLogger(msiName$3); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions$2(scopes, clientId, resourceId) { const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$3}: Multiple scopes are not supported.`); } const body = { resource, }; if (clientId) { body.client_id = clientId; } if (resourceId) { body.msi_res_id = resourceId; } // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below. if (!process.env.MSI_ENDPOINT) { throw new Error(`${msiName$3}: Missing environment variable: MSI_ENDPOINT`); } const params = new URLSearchParams(body); return { url: process.env.MSI_ENDPOINT, method: "POST", body: params.toString(), headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", Metadata: "true", "Content-Type": "application/x-www-form-urlencoded", }), }; } /** * Defines how to determine whether the Azure Cloud Shell MSI is available, and also how to retrieve a token from the Azure Cloud Shell MSI. * Since Azure Managed Identities aren't available in the Azure Cloud Shell, we log a warning for users that try to access cloud shell using user assigned identity. */ const cloudShellMsi = { name: "cloudShellMsi", async isAvailable({ scopes }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$l.info(`${msiName$3}: Unavailable. Multiple scopes are not supported.`); return false; } const result = Boolean(process.env.MSI_ENDPOINT); if (!result) { logger$l.info(`${msiName$3}: Unavailable. The environment variable MSI_ENDPOINT is needed.`); } return result; }, async getToken(configuration, getTokenOptions = {}) { const { identityClient, scopes, clientId, resourceId } = configuration; if (clientId) { logger$l.warning(`${msiName$3}: user-assigned identities not supported. The argument clientId might be ignored by the service.`); } if (resourceId) { logger$l.warning(`${msiName$3}: user defined managed Identity by resource Id not supported. The argument resourceId might be ignored by the service.`); } logger$l.info(`${msiName$3}: Using the endpoint coming form the environment variable MSI_ENDPOINT = ${process.env.MSI_ENDPOINT}.`); const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$2(scopes, clientId, resourceId)), { // Generally, MSI endpoints use the HTTP protocol, without transport layer security (TLS). allowInsecureConnection: true })); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // This MSI can be easily tested by deploying a container to Azure Service Fabric with the Dockerfile: // // FROM node:12 // RUN wget https://host.any/path/bash.sh // CMD ["bash", "bash.sh"] // // Where the bash script contains: // // curl --insecure $IDENTITY_ENDPOINT'?api-version=2019-07-01-preview&resource=https://vault.azure.net/' -H "Secret: $IDENTITY_HEADER" // const msiName$2 = "ManagedIdentityCredential - Fabric MSI"; const logger$k = credentialLogger(msiName$2); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions$1(scopes, clientId, resourceId) { const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$2}: Multiple scopes are not supported.`); } const queryParameters = { resource, "api-version": azureFabricVersion, }; if (clientId) { queryParameters.client_id = clientId; } if (resourceId) { queryParameters.msi_res_id = resourceId; } const query = new URLSearchParams(queryParameters); // This error should not bubble up, since we verify that this environment variable is defined in the isAvailable() method defined below. if (!process.env.IDENTITY_ENDPOINT) { throw new Error("Missing environment variable: IDENTITY_ENDPOINT"); } if (!process.env.IDENTITY_HEADER) { throw new Error("Missing environment variable: IDENTITY_HEADER"); } return { url: `${process.env.IDENTITY_ENDPOINT}?${query.toString()}`, method: "GET", headers: coreRestPipeline.createHttpHeaders({ Accept: "application/json", secret: process.env.IDENTITY_HEADER, }), }; } /** * Defines how to determine whether the Azure Service Fabric MSI is available, and also how to retrieve a token from the Azure Service Fabric MSI. */ const fabricMsi = { name: "fabricMsi", async isAvailable({ scopes }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$k.info(`${msiName$2}: Unavailable. Multiple scopes are not supported.`); return false; } const env = process.env; const result = Boolean(env.IDENTITY_ENDPOINT && env.IDENTITY_HEADER && env.IDENTITY_SERVER_THUMBPRINT); if (!result) { logger$k.info(`${msiName$2}: Unavailable. The environment variables needed are: IDENTITY_ENDPOINT, IDENTITY_HEADER and IDENTITY_SERVER_THUMBPRINT`); } return result; }, async getToken(configuration, getTokenOptions = {}) { const { scopes, identityClient, clientId, resourceId } = configuration; if (resourceId) { logger$k.warning(`${msiName$2}: user defined managed Identity by resource Id is not supported. Argument resourceId might be ignored by the service.`); } logger$k.info([ `${msiName$2}:`, "Using the endpoint and the secret coming from the environment variables:", `IDENTITY_ENDPOINT=${process.env.IDENTITY_ENDPOINT},`, "IDENTITY_HEADER=[REDACTED] and", "IDENTITY_SERVER_THUMBPRINT=[REDACTED].", ].join(" ")); const request = coreRestPipeline.createPipelineRequest(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions$1(scopes, clientId, resourceId))); request.agent = new https.Agent({ // This is necessary because Service Fabric provides a self-signed certificate. // The alternative path is to verify the certificate using the IDENTITY_SERVER_THUMBPRINT env variable. rejectUnauthorized: false, }); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * @internal */ const logger$j = credentialLogger("IdentityUtils"); /** * Latest AuthenticationRecord version * @internal */ const LatestAuthenticationRecordVersion = "1.0"; /** * Ensures the validity of the MSAL token * @internal */ function ensureValidMsalToken(scopes, msalToken, getTokenOptions) { const error = (message) => { logger$j.getToken.info(message); return new AuthenticationRequiredError({ scopes: Array.isArray(scopes) ? scopes : [scopes], getTokenOptions, message, }); }; if (!msalToken) { throw error("No response"); } if (!msalToken.expiresOn) { throw error(`Response had no "expiresOn" property.`); } if (!msalToken.accessToken) { throw error(`Response had no "accessToken" property.`); } } /** * Generates a valid authority by combining a host with a tenantId. * @internal */ function getAuthority(tenantId, host) { if (!host) { host = DefaultAuthorityHost; } if (new RegExp(`${tenantId}/?$`).test(host)) { return host; } if (host.endsWith("/")) { return host + tenantId; } else { return `${host}/${tenantId}`; } } /** * Generates the known authorities. * If the Tenant Id is `adfs`, the authority can't be validated since the format won't match the expected one. * For that reason, we have to force MSAL to disable validating the authority * by sending it within the known authorities in the MSAL configuration. * @internal */ function getKnownAuthorities(tenantId, authorityHost, disableInstanceDiscovery) { if ((tenantId === "adfs" && authorityHost) || disableInstanceDiscovery) { return [authorityHost]; } return []; } /** * Generates a logger that can be passed to the MSAL clients. * @param credLogger - The logger of the credential. * @internal */ const defaultLoggerCallback = (credLogger, platform = coreUtil.isNode ? "Node" : "Browser") => (level, message, containsPii) => { if (containsPii) { return; } switch (level) { case msalCommon__namespace.LogLevel.Error: credLogger.info(`MSAL ${platform} V2 error: ${message}`); return; case msalCommon__namespace.LogLevel.Info: credLogger.info(`MSAL ${platform} V2 info message: ${message}`); return; case msalCommon__namespace.LogLevel.Verbose: credLogger.info(`MSAL ${platform} V2 verbose message: ${message}`); return; case msalCommon__namespace.LogLevel.Warning: credLogger.info(`MSAL ${platform} V2 warning: ${message}`); return; } }; /** * @internal */ function getMSALLogLevel(logLevel) { switch (logLevel) { case "error": return msalCommon__namespace.LogLevel.Error; case "info": return msalCommon__namespace.LogLevel.Info; case "verbose": return msalCommon__namespace.LogLevel.Verbose; case "warning": return msalCommon__namespace.LogLevel.Warning; default: // default msal logging level should be Info return msalCommon__namespace.LogLevel.Info; } } /** * Wraps core-util's randomUUID in order to allow for mocking in tests. * This prepares the library for the upcoming core-util update to ESM. * * @internal * @returns A string containing a random UUID */ function randomUUID() { return coreUtil.randomUUID(); } /** * Handles MSAL errors. */ function handleMsalError(scopes, error, getTokenOptions) { if (error.name === "AuthError" || error.name === "ClientAuthError" || error.name === "BrowserAuthError") { const msalError = error; switch (msalError.errorCode) { case "endpoints_resolution_error": logger$j.info(formatError(scopes, error.message)); return new CredentialUnavailableError(error.message); case "device_code_polling_cancelled": return new abortController.AbortError("The authentication has been aborted by the caller."); case "consent_required": case "interaction_required": case "login_required": logger$j.info(formatError(scopes, `Authentication returned errorCode ${msalError.errorCode}`)); break; default: logger$j.info(formatError(scopes, `Failed to acquire token: ${error.message}`)); break; } } if (error.name === "ClientConfigurationError" || error.name === "BrowserConfigurationAuthError" || error.name === "AbortError") { return error; } if (error.name === "NativeAuthError") { logger$j.info(formatError(scopes, `Error from the native broker: ${error.message} with status code: ${error.statusCode}`)); return error; } return new AuthenticationRequiredError({ scopes, getTokenOptions, message: error.message }); } // transformations.ts function publicToMsal(account) { const [environment] = account.authority.match(/([a-z]*\.[a-z]*\.[a-z]*)/) || [""]; return Object.assign(Object.assign({}, account), { localAccountId: account.homeAccountId, environment }); } function msalToPublic(clientId, account) { const record = { authority: getAuthority(account.tenantId, account.environment), homeAccountId: account.homeAccountId, tenantId: account.tenantId || DefaultTenantId, username: account.username, clientId, version: LatestAuthenticationRecordVersion, }; return record; } /** * Serializes an `AuthenticationRecord` into a string. * * The output of a serialized authentication record will contain the following properties: * * - "authority" * - "homeAccountId" * - "clientId" * - "tenantId" * - "username" * - "version" * * To later convert this string to a serialized `AuthenticationRecord`, please use the exported function `deserializeAuthenticationRecord()`. */ function serializeAuthenticationRecord(record) { return JSON.stringify(record); } /** * Deserializes a previously serialized authentication record from a string into an object. * * The input string must contain the following properties: * * - "authority" * - "homeAccountId" * - "clientId" * - "tenantId" * - "username" * - "version" * * If the version we receive is unsupported, an error will be thrown. * * At the moment, the only available version is: "1.0", which is always set when the authentication record is serialized. * * @param serializedRecord - Authentication record previously serialized into string. * @returns AuthenticationRecord. */ function deserializeAuthenticationRecord(serializedRecord) { const parsed = JSON.parse(serializedRecord); if (parsed.version && parsed.version !== LatestAuthenticationRecordVersion) { throw Error("Unsupported AuthenticationRecord version"); } return parsed; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName$1 = "ManagedIdentityCredential - IMDS"; const logger$i = credentialLogger(msiName$1); /** * Generates the options used on the request for an access token. */ function prepareRequestOptions(scopes, clientId, resourceId, options) { var _a; const resource = mapScopesToResource(scopes); if (!resource) { throw new Error(`${msiName$1}: Multiple scopes are not supported.`); } const { skipQuery, skipMetadataHeader } = options || {}; let query = ""; // Pod Identity will try to process this request even if the Metadata header is missing. // We can exclude the request query to ensure no IMDS endpoint tries to process the ping request. if (!skipQuery) { const queryParameters = { resource, "api-version": imdsApiVersion, }; if (clientId) { queryParameters.client_id = clientId; } if (resourceId) { queryParameters.msi_res_id = resourceId; } const params = new URLSearchParams(queryParameters); query = `?${params.toString()}`; } const url = new URL(imdsEndpointPath, (_a = process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) !== null && _a !== void 0 ? _a : imdsHost); const rawHeaders = { Accept: "application/json", Metadata: "true", }; // Remove the Metadata header to invoke a request error from some IMDS endpoints. if (skipMetadataHeader) { delete rawHeaders.Metadata; } return { // In this case, the `?` should be added in the "query" variable `skipQuery` is not set. url: `${url}${query}`, method: "GET", headers: coreRestPipeline.createHttpHeaders(rawHeaders), }; } /** * Defines how to determine whether the Azure IMDS MSI is available, and also how to retrieve a token from the Azure IMDS MSI. */ const imdsMsi = { name: "imdsMsi", async isAvailable({ scopes, identityClient, clientId, resourceId, getTokenOptions = {}, }) { const resource = mapScopesToResource(scopes); if (!resource) { logger$i.info(`${msiName$1}: Unavailable. Multiple scopes are not supported.`); return false; } // if the PodIdentityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) { return true; } if (!identityClient) { throw new Error("Missing IdentityClient"); } const requestOptions = prepareRequestOptions(resource, clientId, resourceId, { skipMetadataHeader: true, skipQuery: true, }); return tracingClient.withSpan("ManagedIdentityCredential-pingImdsEndpoint", getTokenOptions, async (options) => { var _a, _b; requestOptions.tracingOptions = options.tracingOptions; // Create a request with a timeout since we expect that // not having a "Metadata" header should cause an error to be // returned quickly from the endpoint, proving its availability. const request = coreRestPipeline.createPipelineRequest(requestOptions); // Default to 1000 if the default of 0 is used. // Negative values can still be used to disable the timeout. request.timeout = ((_a = options.requestOptions) === null || _a === void 0 ? void 0 : _a.timeout) || 1000; // This MSI uses the imdsEndpoint to get the token, which only uses http:// request.allowInsecureConnection = true; let response; try { logger$i.info(`${msiName$1}: Pinging the Azure IMDS endpoint`); response = await identityClient.sendRequest(request); } catch (err) { // If the request failed, or Node.js was unable to establish a connection, // or the host was down, we'll assume the IMDS endpoint isn't available. if (coreUtil.isError(err)) { logger$i.verbose(`${msiName$1}: Caught error ${err.name}: ${err.message}`); } // This is a special case for Docker Desktop which responds with a 403 with a message that contains "A socket operation was attempted to an unreachable network" or "A socket operation was attempted to an unreachable host" // rather than just timing out, as expected. logger$i.info(`${msiName$1}: The Azure IMDS endpoint is unavailable`); return false; } if (response.status === 403) { if ((_b = response.bodyAsText) === null || _b === void 0 ? void 0 : _b.includes("unreachable")) { logger$i.info(`${msiName$1}: The Azure IMDS endpoint is unavailable`); logger$i.info(`${msiName$1}: ${response.bodyAsText}`); return false; } } // If we received any response, the endpoint is available logger$i.info(`${msiName$1}: The Azure IMDS endpoint is available`); return true; }); }, async getToken(configuration, getTokenOptions = {}) { const { identityClient, scopes, clientId, resourceId } = configuration; if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) { logger$i.info(`${msiName$1}: Using the Azure IMDS endpoint coming from the environment variable AZURE_POD_IDENTITY_AUTHORITY_HOST=${process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST}.`); } else { logger$i.info(`${msiName$1}: Using the default Azure IMDS endpoint ${imdsHost}.`); } let nextDelayInMs = configuration.retryConfig.startDelayInMs; for (let retries = 0; retries < configuration.retryConfig.maxRetries; retries++) { try { const request = coreRestPipeline.createPipelineRequest(Object.assign(Object.assign({ abortSignal: getTokenOptions.abortSignal }, prepareRequestOptions(scopes, clientId, resourceId)), { allowInsecureConnection: true })); const tokenResponse = await identityClient.sendTokenRequest(request); return (tokenResponse && tokenResponse.accessToken) || null; } catch (error) { if (error.statusCode === 404) { await coreUtil.delay(nextDelayInMs); nextDelayInMs *= configuration.retryConfig.intervalIncrement; continue; } throw error; } } throw new AuthenticationError(404, `${msiName$1}: Failed to retrieve IMDS token after ${configuration.retryConfig.maxRetries} retries.`); }, }; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Helps specify a regional authority, or "AutoDiscoverRegion" to auto-detect the region. */ var RegionalAuthority; (function (RegionalAuthority) { /** Instructs MSAL to attempt to discover the region */ RegionalAuthority["AutoDiscoverRegion"] = "AutoDiscoverRegion"; /** Uses the {@link RegionalAuthority} for the Azure 'westus' region. */ RegionalAuthority["USWest"] = "westus"; /** Uses the {@link RegionalAuthority} for the Azure 'westus2' region. */ RegionalAuthority["USWest2"] = "westus2"; /** Uses the {@link RegionalAuthority} for the Azure 'centralus' region. */ RegionalAuthority["USCentral"] = "centralus"; /** Uses the {@link RegionalAuthority} for the Azure 'eastus' region. */ RegionalAuthority["USEast"] = "eastus"; /** Uses the {@link RegionalAuthority} for the Azure 'eastus2' region. */ RegionalAuthority["USEast2"] = "eastus2"; /** Uses the {@link RegionalAuthority} for the Azure 'northcentralus' region. */ RegionalAuthority["USNorthCentral"] = "northcentralus"; /** Uses the {@link RegionalAuthority} for the Azure 'southcentralus' region. */ RegionalAuthority["USSouthCentral"] = "southcentralus"; /** Uses the {@link RegionalAuthority} for the Azure 'westcentralus' region. */ RegionalAuthority["USWestCentral"] = "westcentralus"; /** Uses the {@link RegionalAuthority} for the Azure 'canadacentral' region. */ RegionalAuthority["CanadaCentral"] = "canadacentral"; /** Uses the {@link RegionalAuthority} for the Azure 'canadaeast' region. */ RegionalAuthority["CanadaEast"] = "canadaeast"; /** Uses the {@link RegionalAuthority} for the Azure 'brazilsouth' region. */ RegionalAuthority["BrazilSouth"] = "brazilsouth"; /** Uses the {@link RegionalAuthority} for the Azure 'northeurope' region. */ RegionalAuthority["EuropeNorth"] = "northeurope"; /** Uses the {@link RegionalAuthority} for the Azure 'westeurope' region. */ RegionalAuthority["EuropeWest"] = "westeurope"; /** Uses the {@link RegionalAuthority} for the Azure 'uksouth' region. */ RegionalAuthority["UKSouth"] = "uksouth"; /** Uses the {@link RegionalAuthority} for the Azure 'ukwest' region. */ RegionalAuthority["UKWest"] = "ukwest"; /** Uses the {@link RegionalAuthority} for the Azure 'francecentral' region. */ RegionalAuthority["FranceCentral"] = "francecentral"; /** Uses the {@link RegionalAuthority} for the Azure 'francesouth' region. */ RegionalAuthority["FranceSouth"] = "francesouth"; /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandnorth' region. */ RegionalAuthority["SwitzerlandNorth"] = "switzerlandnorth"; /** Uses the {@link RegionalAuthority} for the Azure 'switzerlandwest' region. */ RegionalAuthority["SwitzerlandWest"] = "switzerlandwest"; /** Uses the {@link RegionalAuthority} for the Azure 'germanynorth' region. */ RegionalAuthority["GermanyNorth"] = "germanynorth"; /** Uses the {@link RegionalAuthority} for the Azure 'germanywestcentral' region. */ RegionalAuthority["GermanyWestCentral"] = "germanywestcentral"; /** Uses the {@link RegionalAuthority} for the Azure 'norwaywest' region. */ RegionalAuthority["NorwayWest"] = "norwaywest"; /** Uses the {@link RegionalAuthority} for the Azure 'norwayeast' region. */ RegionalAuthority["NorwayEast"] = "norwayeast"; /** Uses the {@link RegionalAuthority} for the Azure 'eastasia' region. */ RegionalAuthority["AsiaEast"] = "eastasia"; /** Uses the {@link RegionalAuthority} for the Azure 'southeastasia' region. */ RegionalAuthority["AsiaSouthEast"] = "southeastasia"; /** Uses the {@link RegionalAuthority} for the Azure 'japaneast' region. */ RegionalAuthority["JapanEast"] = "japaneast"; /** Uses the {@link RegionalAuthority} for the Azure 'japanwest' region. */ RegionalAuthority["JapanWest"] = "japanwest"; /** Uses the {@link RegionalAuthority} for the Azure 'australiaeast' region. */ RegionalAuthority["AustraliaEast"] = "australiaeast"; /** Uses the {@link RegionalAuthority} for the Azure 'australiasoutheast' region. */ RegionalAuthority["AustraliaSouthEast"] = "australiasoutheast"; /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral' region. */ RegionalAuthority["AustraliaCentral"] = "australiacentral"; /** Uses the {@link RegionalAuthority} for the Azure 'australiacentral2' region. */ RegionalAuthority["AustraliaCentral2"] = "australiacentral2"; /** Uses the {@link RegionalAuthority} for the Azure 'centralindia' region. */ RegionalAuthority["IndiaCentral"] = "centralindia"; /** Uses the {@link RegionalAuthority} for the Azure 'southindia' region. */ RegionalAuthority["IndiaSouth"] = "southindia"; /** Uses the {@link RegionalAuthority} for the Azure 'westindia' region. */ RegionalAuthority["IndiaWest"] = "westindia"; /** Uses the {@link RegionalAuthority} for the Azure 'koreasouth' region. */ RegionalAuthority["KoreaSouth"] = "koreasouth"; /** Uses the {@link RegionalAuthority} for the Azure 'koreacentral' region. */ RegionalAuthority["KoreaCentral"] = "koreacentral"; /** Uses the {@link RegionalAuthority} for the Azure 'uaecentral' region. */ RegionalAuthority["UAECentral"] = "uaecentral"; /** Uses the {@link RegionalAuthority} for the Azure 'uaenorth' region. */ RegionalAuthority["UAENorth"] = "uaenorth"; /** Uses the {@link RegionalAuthority} for the Azure 'southafricanorth' region. */ RegionalAuthority["SouthAfricaNorth"] = "southafricanorth"; /** Uses the {@link RegionalAuthority} for the Azure 'southafricawest' region. */ RegionalAuthority["SouthAfricaWest"] = "southafricawest"; /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth' region. */ RegionalAuthority["ChinaNorth"] = "chinanorth"; /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast' region. */ RegionalAuthority["ChinaEast"] = "chinaeast"; /** Uses the {@link RegionalAuthority} for the Azure 'chinanorth2' region. */ RegionalAuthority["ChinaNorth2"] = "chinanorth2"; /** Uses the {@link RegionalAuthority} for the Azure 'chinaeast2' region. */ RegionalAuthority["ChinaEast2"] = "chinaeast2"; /** Uses the {@link RegionalAuthority} for the Azure 'germanycentral' region. */ RegionalAuthority["GermanyCentral"] = "germanycentral"; /** Uses the {@link RegionalAuthority} for the Azure 'germanynortheast' region. */ RegionalAuthority["GermanyNorthEast"] = "germanynortheast"; /** Uses the {@link RegionalAuthority} for the Azure 'usgovvirginia' region. */ RegionalAuthority["GovernmentUSVirginia"] = "usgovvirginia"; /** Uses the {@link RegionalAuthority} for the Azure 'usgoviowa' region. */ RegionalAuthority["GovernmentUSIowa"] = "usgoviowa"; /** Uses the {@link RegionalAuthority} for the Azure 'usgovarizona' region. */ RegionalAuthority["GovernmentUSArizona"] = "usgovarizona"; /** Uses the {@link RegionalAuthority} for the Azure 'usgovtexas' region. */ RegionalAuthority["GovernmentUSTexas"] = "usgovtexas"; /** Uses the {@link RegionalAuthority} for the Azure 'usdodeast' region. */ RegionalAuthority["GovernmentUSDodEast"] = "usdodeast"; /** Uses the {@link RegionalAuthority} for the Azure 'usdodcentral' region. */ RegionalAuthority["GovernmentUSDodCentral"] = "usdodcentral"; })(RegionalAuthority || (RegionalAuthority = {})); /** * Calculates the correct regional authority based on the supplied value * and the AZURE_REGIONAL_AUTHORITY_NAME environment variable. * * Values will be returned verbatim, except for {@link RegionalAuthority.AutoDiscoverRegion} * which is mapped to a value MSAL can understand. * * @internal */ function calculateRegionalAuthority(regionalAuthority) { // Note: as of today only 3 credentials support regional authority, and the parameter // is not exposed via the public API. Regional Authority is _only_ supported // via the AZURE_REGIONAL_AUTHORITY_NAME env var and _only_ for: ClientSecretCredential, ClientCertificateCredential, and ClientAssertionCredential. var _a, _b; // Accepting the regionalAuthority parameter will allow us to support it in the future. let azureRegion = regionalAuthority; if (azureRegion === undefined && ((_b = (_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.AZURE_REGIONAL_AUTHORITY_NAME) !== undefined) { azureRegion = process.env.AZURE_REGIONAL_AUTHORITY_NAME; } if (azureRegion === RegionalAuthority.AutoDiscoverRegion) { return "AUTO_DISCOVER"; } return azureRegion; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * The default logger used if no logger was passed in by the credential. */ const msalLogger = credentialLogger("MsalClient"); /** * Generates the configuration for MSAL (Microsoft Authentication Library). * * @param clientId - The client ID of the application. * @param tenantId - The tenant ID of the Azure Active Directory. * @param msalClientOptions - Optional. Additional options for creating the MSAL client. * @returns The MSAL configuration object. */ function generateMsalConfiguration(clientId, tenantId, msalClientOptions = {}) { var _a, _b, _c, _d; const resolvedTenant = resolveTenantId((_a = msalClientOptions.logger) !== null && _a !== void 0 ? _a : msalLogger, tenantId, clientId); // TODO: move and reuse getIdentityClientAuthorityHost const authority = getAuthority(resolvedTenant, (_b = msalClientOptions.authorityHost) !== null && _b !== void 0 ? _b : process.env.AZURE_AUTHORITY_HOST); const httpClient = new IdentityClient(Object.assign(Object.assign({}, msalClientOptions.tokenCredentialOptions), { authorityHost: authority, loggingOptions: msalClientOptions.loggingOptions })); const msalConfig = { auth: { clientId, authority, knownAuthorities: getKnownAuthorities(resolvedTenant, authority, msalClientOptions.disableInstanceDiscovery), }, system: { networkClient: httpClient, loggerOptions: { loggerCallback: defaultLoggerCallback((_c = msalClientOptions.logger) !== null && _c !== void 0 ? _c : msalLogger), logLevel: getMSALLogLevel(logger$r.getLogLevel()), piiLoggingEnabled: (_d = msalClientOptions.loggingOptions) === null || _d === void 0 ? void 0 : _d.enableUnsafeSupportLogging, }, }, }; return msalConfig; } /** * Creates an instance of the MSAL (Microsoft Authentication Library) client. * * @param clientId - The client ID of the application. * @param tenantId - The tenant ID of the Azure Active Directory. * @param createMsalClientOptions - Optional. Additional options for creating the MSAL client. * @returns An instance of the MSAL client. * * @public */ function createMsalClient(clientId, tenantId, createMsalClientOptions = {}) { var _a; const state = { msalConfig: generateMsalConfiguration(clientId, tenantId, createMsalClientOptions), cachedAccount: createMsalClientOptions.authenticationRecord ? publicToMsal(createMsalClientOptions.authenticationRecord) : null, pluginConfiguration: msalPlugins.generatePluginConfiguration(createMsalClientOptions), logger: (_a = createMsalClientOptions.logger) !== null && _a !== void 0 ? _a : msalLogger, }; const publicApps = new Map(); async function getPublicApp(options = {}) { const appKey = options.enableCae ? "CAE" : "default"; let publicClientApp = publicApps.get(appKey); if (publicClientApp) { state.logger.getToken.info("Existing PublicClientApplication found in cache, returning it."); return publicClientApp; } // Initialize a new app and cache it state.logger.getToken.info(`Creating new PublicClientApplication with CAE ${options.enableCae ? "enabled" : "disabled"}.`); const cachePlugin = options.enableCae ? state.pluginConfiguration.cache.cachePluginCae : state.pluginConfiguration.cache.cachePlugin; state.msalConfig.auth.clientCapabilities = options.enableCae ? ["cp1"] : undefined; publicClientApp = new msalCommon__namespace.PublicClientApplication(Object.assign(Object.assign({}, state.msalConfig), { broker: { nativeBrokerPlugin: state.pluginConfiguration.broker.nativeBrokerPlugin }, cache: { cachePlugin: await cachePlugin } })); publicApps.set(appKey, publicClientApp); return publicClientApp; } const confidentialApps = new Map(); async function getConfidentialApp(options = {}) { const appKey = options.enableCae ? "CAE" : "default"; let confidentialClientApp = confidentialApps.get(appKey); if (confidentialClientApp) { state.logger.getToken.info("Existing ConfidentialClientApplication found in cache, returning it."); return confidentialClientApp; } // Initialize a new app and cache it state.logger.getToken.info(`Creating new ConfidentialClientApplication with CAE ${options.enableCae ? "enabled" : "disabled"}.`); const cachePlugin = options.enableCae ? state.pluginConfiguration.cache.cachePluginCae : state.pluginConfiguration.cache.cachePlugin; state.msalConfig.auth.clientCapabilities = options.enableCae ? ["cp1"] : undefined; confidentialClientApp = new msalCommon__namespace.ConfidentialClientApplication(Object.assign(Object.assign({}, state.msalConfig), { broker: { nativeBrokerPlugin: state.pluginConfiguration.broker.nativeBrokerPlugin }, cache: { cachePlugin: await cachePlugin } })); confidentialApps.set(appKey, confidentialClientApp); return confidentialClientApp; } async function getTokenSilent(app, scopes, options = {}) { if (state.cachedAccount === null) { state.logger.getToken.info("No cached account found in local state, attempting to load it from MSAL cache."); const cache = app.getTokenCache(); const accounts = await cache.getAllAccounts(); if (accounts === undefined || accounts.length === 0) { throw new AuthenticationRequiredError({ scopes }); } if (accounts.length > 1) { state.logger .info(`More than one account was found authenticated for this Client ID and Tenant ID. However, no "authenticationRecord" has been provided for this credential, therefore we're unable to pick between these accounts. A new login attempt will be requested, to ensure the correct account is picked. 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.`); throw new AuthenticationRequiredError({ scopes }); } state.cachedAccount = accounts[0]; } // Keep track and reuse the claims we received across challenges if (options.claims) { state.cachedClaims = options.claims; } const silentRequest = { account: state.cachedAccount, scopes, claims: state.cachedClaims, }; if (state.pluginConfiguration.broker.isEnabled) { silentRequest.tokenQueryParameters || (silentRequest.tokenQueryParameters = {}); if (state.pluginConfiguration.broker.enableMsaPassthrough) { silentRequest.tokenQueryParameters["msal_request_type"] = "consumer_passthrough"; } } state.logger.getToken.info("Attempting to acquire token silently"); return app.acquireTokenSilent(silentRequest); } /** * Performs silent authentication using MSAL to acquire an access token. * If silent authentication fails, falls back to interactive authentication. * * @param msalApp - The MSAL application instance. * @param scopes - The scopes for which to acquire the access token. * @param options - The options for acquiring the access token. * @param onAuthenticationRequired - A callback function to handle interactive authentication when silent authentication fails. * @returns A promise that resolves to an AccessToken object containing the access token and its expiration timestamp. */ async function withSilentAuthentication(msalApp, scopes, options, onAuthenticationRequired) { var _a; let response = null; try { response = await getTokenSilent(msalApp, scopes, options); } catch (e) { if (e.name !== "AuthenticationRequiredError") { throw e; } if (options.disableAutomaticAuthentication) { throw new AuthenticationRequiredError({ scopes, getTokenOptions: options, message: "Automatic authentication has been disabled. You may call the authentication() method.", }); } } // Silent authentication failed if (response === null) { try { response = await onAuthenticationRequired(); } catch (err) { throw handleMsalError(scopes, err, options); } } // At this point we should have a token, process it ensureValidMsalToken(scopes, response, options); state.cachedAccount = (_a = response === null || response === void 0 ? void 0 : response.account) !== null && _a !== void 0 ? _a : null; state.logger.getToken.info(formatSuccess(scopes)); return { token: response.accessToken, expiresOnTimestamp: response.expiresOn.getTime(), }; } async function getTokenByClientSecret(scopes, clientSecret, options = {}) { state.logger.getToken.info(`Attempting to acquire token using client secret`); state.msalConfig.auth.clientSecret = clientSecret; const msalApp = await getConfidentialApp(options); try { const response = await msalApp.acquireTokenByClientCredential({ scopes, authority: state.msalConfig.auth.authority, azureRegion: calculateRegionalAuthority(), claims: options === null || options === void 0 ? void 0 : options.claims, }); ensureValidMsalToken(scopes, response, options); state.logger.getToken.info(formatSuccess(scopes)); return { token: response.accessToken, expiresOnTimestamp: response.expiresOn.getTime(), }; } catch (err) { throw handleMsalError(scopes, err, options); } } async function getTokenByClientAssertion(scopes, clientAssertion, options = {}) { state.logger.getToken.info(`Attempting to acquire token using client assertion`); state.msalConfig.auth.clientAssertion = clientAssertion; const msalApp = await getConfidentialApp(options); try { const response = await msalApp.acquireTokenByClientCredential({ scopes, authority: state.msalConfig.auth.authority, azureRegion: calculateRegionalAuthority(), claims: options === null || options === void 0 ? void 0 : options.claims, clientAssertion, }); ensureValidMsalToken(scopes, response, options); state.logger.getToken.info(formatSuccess(scopes)); return { token: response.accessToken, expiresOnTimestamp: response.expiresOn.getTime(), }; } catch (err) { throw handleMsalError(scopes, err, options); } } async function getTokenByClientCertificate(scopes, certificate, options = {}) { state.logger.getToken.info(`Attempting to acquire token using client certificate`); state.msalConfig.auth.clientCertificate = certificate; const msalApp = await getConfidentialApp(options); try { const response = await msalApp.acquireTokenByClientCredential({ scopes, authority: state.msalConfig.auth.authority, azureRegion: calculateRegionalAuthority(), claims: options === null || options === void 0 ? void 0 : options.claims, }); ensureValidMsalToken(scopes, response, options); state.logger.getToken.info(formatSuccess(scopes)); return { token: response.accessToken, expiresOnTimestamp: response.expiresOn.getTime(), }; } catch (err) { throw handleMsalError(scopes, err, options); } } async function getTokenByDeviceCode(scopes, deviceCodeCallback, options = {}) { state.logger.getToken.info(`Attempting to acquire token using device code`); const msalApp = await getPublicApp(options); return withSilentAuthentication(msalApp, scopes, options, () => { var _a, _b; const requestOptions = { scopes, cancel: (_b = (_a = options === null || options === void 0 ? void 0 : options.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) !== null && _b !== void 0 ? _b : false, deviceCodeCallback, authority: state.msalConfig.auth.authority, claims: options === null || options === void 0 ? void 0 : options.claims, }; const deviceCodeRequest = msalApp.acquireTokenByDeviceCode(requestOptions); if (options.abortSignal) { options.abortSignal.addEventListener("abort", () => { requestOptions.cancel = true; }); } return deviceCodeRequest; }); } async function getTokenByUsernamePassword(scopes, username, password, options = {}) { state.logger.getToken.info(`Attempting to acquire token using username and password`); const msalApp = await getPublicApp(options); return withSilentAuthentication(msalApp, scopes, options, () => { const requestOptions = { scopes, username, password, authority: state.msalConfig.auth.authority, claims: options === null || options === void 0 ? void 0 : options.claims, }; return msalApp.acquireTokenByUsernamePassword(requestOptions); }); } function getActiveAccount() { if (!state.cachedAccount) { return undefined; } return msalToPublic(clientId, state.cachedAccount); } async function getTokenByAuthorizationCode(scopes, redirectUri, authorizationCode, clientSecret, options = {}) { state.logger.getToken.info(`Attempting to acquire token using authorization code`); let msalApp; if (clientSecret) { // If a client secret is provided, we need to use a confidential client application // See https://learn.microsoft.com/entra/identity-platform/v2-oauth2-auth-code-flow#request-an-access-token-with-a-client_secret state.msalConfig.auth.clientSecret = clientSecret; msalApp = await getConfidentialApp(options); } else { msalApp = await getPublicApp(options); } return withSilentAuthentication(msalApp, scopes, options, () => { return msalApp.acquireTokenByCode({ scopes, redirectUri, code: authorizationCode, authority: state.msalConfig.auth.authority, claims: options === null || options === void 0 ? void 0 : options.claims, }); }); } return { getActiveAccount, getTokenByClientSecret, getTokenByClientAssertion, getTokenByClientCertificate, getTokenByDeviceCode, getTokenByUsernamePassword, getTokenByAuthorizationCode, }; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const logger$h = credentialLogger("ClientAssertionCredential"); /** * Authenticates a service principal with a JWT assertion. */ class ClientAssertionCredential { /** * Creates an instance of the ClientAssertionCredential with the details * needed to authenticate against Microsoft Entra ID with a client * assertion provided by the developer through the `getAssertion` function parameter. * * @param tenantId - The Microsoft Entra tenant (directory) ID. * @param clientId - The client (application) ID of an App Registration in the tenant. * @param getAssertion - A function that retrieves the assertion for the credential to use. * @param options - Options for configuring the client which makes the authentication request. */ constructor(tenantId, clientId, getAssertion, options = {}) { if (!tenantId || !clientId || !getAssertion) { throw new Error("ClientAssertionCredential: tenantId, clientId, and clientAssertion are required parameters."); } this.tenantId = tenantId; this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants); this.options = options; this.getAssertion = getAssertion; this.msalClient = createMsalClient(clientId, tenantId, Object.assign(Object.assign({}, options), { logger: logger$h, tokenCredentialOptions: this.options })); } /** * Authenticates with Microsoft Entra ID and returns an access token if successful. * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure. * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ async getToken(scopes, options = {}) { return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async (newOptions) => { newOptions.tenantId = processMultiTenantRequest(this.tenantId, newOptions, this.additionallyAllowedTenantIds, logger$h); const clientAssertion = await this.getAssertion(); const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalClient.getTokenByClientAssertion(arrayScopes, clientAssertion, newOptions); }); } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const credentialName$4 = "WorkloadIdentityCredential"; /** * Contains the list of all supported environment variable names so that an * appropriate error message can be generated when no credentials can be * configured. * * @internal */ const SupportedWorkloadEnvironmentVariables = [ "AZURE_TENANT_ID", "AZURE_CLIENT_ID", "AZURE_FEDERATED_TOKEN_FILE", ]; const logger$g = credentialLogger(credentialName$4); /** * Workload Identity authentication is a feature in Azure that allows applications running on virtual machines (VMs) * to access other Azure resources without the need for a service principal or managed identity. With Workload Identity * authentication, applications authenticate themselves using their own identity, rather than using a shared service * principal or managed identity. Under the hood, Workload Identity authentication uses the concept of Service Account * Credentials (SACs), which are automatically created by Azure and stored securely in the VM. By using Workload * Identity authentication, you can avoid the need to manage and rotate service principals or managed identities for * each application on each VM. Additionally, because SACs are created automatically and managed by Azure, you don't * need to worry about storing and securing sensitive credentials themselves. * The WorkloadIdentityCredential supports Microsoft Entra Workload ID authentication on Azure Kubernetes and acquires * a token using the SACs available in the Azure Kubernetes environment. * Refer to Microsoft Entra * Workload ID for more information. */ class WorkloadIdentityCredential { /** * WorkloadIdentityCredential supports Microsoft Entra Workload ID on Kubernetes. * * @param options - The identity client options to use for authentication. */ constructor(options) { this.azureFederatedTokenFileContent = undefined; this.cacheDate = undefined; // Logging environment variables for error details const assignedEnv = processEnvVars(SupportedWorkloadEnvironmentVariables).assigned.join(", "); logger$g.info(`Found the following environment variables: ${assignedEnv}`); const workloadIdentityCredentialOptions = options !== null && options !== void 0 ? options : {}; const tenantId = workloadIdentityCredentialOptions.tenantId || process.env.AZURE_TENANT_ID; const clientId = workloadIdentityCredentialOptions.clientId || process.env.AZURE_CLIENT_ID; this.federatedTokenFilePath = workloadIdentityCredentialOptions.tokenFilePath || process.env.AZURE_FEDERATED_TOKEN_FILE; if (tenantId) { checkTenantId(logger$g, tenantId); } if (clientId && tenantId && this.federatedTokenFilePath) { logger$g.info(`Invoking ClientAssertionCredential with tenant ID: ${tenantId}, clientId: ${workloadIdentityCredentialOptions.clientId} and federated token path: [REDACTED]`); this.client = new ClientAssertionCredential(tenantId, clientId, this.readFileContents.bind(this), options); } } /** * Authenticates with Microsoft Entra ID and returns an access token if successful. * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure. * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ async getToken(scopes, options) { if (!this.client) { const errorMessage = `${credentialName$4}: is unavailable. tenantId, clientId, and federatedTokenFilePath are required parameters. In DefaultAzureCredential and ManagedIdentityCredential, these can be provided as environment variables - "AZURE_TENANT_ID", "AZURE_CLIENT_ID", "AZURE_FEDERATED_TOKEN_FILE". See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/workloadidentitycredential/troubleshoot `; logger$g.info(errorMessage); throw new CredentialUnavailableError(errorMessage); } logger$g.info("Invoking getToken() of Client Assertion Credential"); return this.client.getToken(scopes, options); } async readFileContents() { // Cached assertions expire after 5 minutes if (this.cacheDate !== undefined && Date.now() - this.cacheDate >= 1000 * 60 * 5) { this.azureFederatedTokenFileContent = undefined; } if (!this.federatedTokenFilePath) { throw new CredentialUnavailableError(`${credentialName$4}: is unavailable. Invalid file path provided ${this.federatedTokenFilePath}.`); } if (!this.azureFederatedTokenFileContent) { const file = await promises.readFile(this.federatedTokenFilePath, "utf8"); const value = file.trim(); if (!value) { throw new CredentialUnavailableError(`${credentialName$4}: is unavailable. No content on the file ${this.federatedTokenFilePath}.`); } else { this.azureFederatedTokenFileContent = value; this.cacheDate = Date.now(); } } return this.azureFederatedTokenFileContent; } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const msiName = "ManagedIdentityCredential - Token Exchange"; const logger$f = credentialLogger(msiName); /** * Defines how to determine whether the token exchange MSI is available, and also how to retrieve a token from the token exchange MSI. */ function tokenExchangeMsi() { return { name: "tokenExchangeMsi", async isAvailable({ clientId }) { const env = process.env; const result = Boolean((clientId || env.AZURE_CLIENT_ID) && env.AZURE_TENANT_ID && process.env.AZURE_FEDERATED_TOKEN_FILE); if (!result) { logger$f.info(`${msiName}: Unavailable. The environment variables needed are: AZURE_CLIENT_ID (or the client ID sent through the parameters), AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE`); } return result; }, async getToken(configuration, getTokenOptions = {}) { const { scopes, clientId } = configuration; const identityClientTokenCredentialOptions = {}; const workloadIdentityCredential = new WorkloadIdentityCredential(Object.assign(Object.assign({ clientId, tenantId: process.env.AZURE_TENANT_ID, tokenFilePath: process.env.AZURE_FEDERATED_TOKEN_FILE }, identityClientTokenCredentialOptions), { disableInstanceDiscovery: true })); const token = await workloadIdentityCredential.getToken(scopes, getTokenOptions); return token; }, }; } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. const logger$e = credentialLogger("ManagedIdentityCredential"); /** * Attempts authentication using a managed identity available at the deployment environment. * This authentication type works in Azure VMs, App Service instances, Azure Functions applications, * Azure Kubernetes Services, Azure Service Fabric instances and inside of the Azure Cloud Shell. * * More information about configuring managed identities can be found here: * https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview */ class ManagedIdentityCredential { /** * @internal * @hidden */ constructor(clientIdOrOptions, options) { var _a, _b; this.isEndpointUnavailable = null; this.isAppTokenProviderInitialized = false; this.msiRetryConfig = { maxRetries: 5, startDelayInMs: 800, intervalIncrement: 2, }; let _options; if (typeof clientIdOrOptions === "string") { this.clientId = clientIdOrOptions; _options = options; } else { this.clientId = clientIdOrOptions === null || clientIdOrOptions === void 0 ? void 0 : clientIdOrOptions.clientId; _options = clientIdOrOptions; } this.resourceId = _options === null || _options === void 0 ? void 0 : _options.resourceId; // For JavaScript users. if (this.clientId && this.resourceId) { throw new Error(`${ManagedIdentityCredential.name} - Client Id and Resource Id can't be provided at the same time.`); } if (((_a = _options === null || _options === void 0 ? void 0 : _options.retryOptions) === null || _a === void 0 ? void 0 : _a.maxRetries) !== undefined) { this.msiRetryConfig.maxRetries = _options.retryOptions.maxRetries; } this.identityClient = new IdentityClient(_options); this.isAvailableIdentityClient = new IdentityClient(Object.assign(Object.assign({}, _options), { retryOptions: { maxRetries: 0, } })); /** authority host validation and metadata discovery to be skipped in managed identity * since this wasn't done previously before adding token cache support */ this.confidentialApp = new msalCommon.ConfidentialClientApplication({ auth: { authority: "https://login.microsoftonline.com/managed_identity", clientId: (_b = this.clientId) !== null && _b !== void 0 ? _b : DeveloperSignOnClientId, clientSecret: "dummy-secret", cloudDiscoveryMetadata: '{"tenant_discovery_endpoint":"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}', authorityMetadata: '{"token_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/common/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/{tenantid}/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/common/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"kerberos_endpoint":"https://login.microsoftonline.com/common/kerberos","tenant_region_scope":null,"cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}', clientCapabilities: [], }, system: { loggerOptions: { logLevel: getMSALLogLevel(logger$r.getLogLevel()), }, }, }); } async cachedAvailableMSI(scopes, getTokenOptions) { if (this.cachedMSI) { return this.cachedMSI; } const MSIs = [ arcMsi, fabricMsi, appServiceMsi2019, appServiceMsi2017, cloudShellMsi, tokenExchangeMsi(), imdsMsi, ]; for (const msi of MSIs) { if (await msi.isAvailable({ scopes, identityClient: this.isAvailableIdentityClient, clientId: this.clientId, resourceId: this.resourceId, getTokenOptions, })) { this.cachedMSI = msi; return msi; } } throw new CredentialUnavailableError(`${ManagedIdentityCredential.name} - No MSI credential available`); } async authenticateManagedIdentity(scopes, getTokenOptions) { const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.authenticateManagedIdentity`, getTokenOptions); try { // Determining the available MSI, and avoiding checking for other MSIs while the program is running. const availableMSI = await this.cachedAvailableMSI(scopes, updatedOptions); return availableMSI.getToken({ identityClient: this.identityClient, scopes, clientId: this.clientId, resourceId: this.resourceId, retryConfig: this.msiRetryConfig, }, updatedOptions); } catch (err) { span.setStatus({ status: "error", error: err, }); throw err; } finally { span.end(); } } /** * Authenticates with Microsoft Entra ID and returns an access token if successful. * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure. * If an unexpected error occurs, an {@link AuthenticationError} will be thrown with the details of the failure. * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ async getToken(scopes, options) { let result = null; const { span, updatedOptions } = tracingClient.startSpan(`${ManagedIdentityCredential.name}.getToken`, options); try { // isEndpointAvailable can be true, false, or null, // If it's null, it means we don't yet know whether // the endpoint is available and need to check for it. if (this.isEndpointUnavailable !== true) { const availableMSI = await this.cachedAvailableMSI(scopes, updatedOptions); if (availableMSI.name === "tokenExchangeMsi") { result = await this.authenticateManagedIdentity(scopes, updatedOptions); } else { const appTokenParameters = { correlationId: this.identityClient.getCorrelationId(), tenantId: (options === null || options === void 0 ? void 0 : options.tenantId) || "managed_identity", scopes: Array.isArray(scopes) ? scopes : [scopes], claims: options === null || options === void 0 ? void 0 : options.claims, }; // Added a check to see if SetAppTokenProvider was already defined. this.initializeSetAppTokenProvider(); const authenticationResult = await this.confidentialApp.acquireTokenByClientCredential(Object.assign({}, appTokenParameters)); result = this.handleResult(scopes, authenticationResult || undefined); } if (result === null) { // If authenticateManagedIdentity returns null, // it means no MSI endpoints are available. // If so, we avoid trying to reach to them in future requests. this.isEndpointUnavailable = true; // It also means that the endpoint answered with either 200 or 201 (see the sendTokenRequest method), // yet we had no access token. For this reason, we'll throw once with a specific message: const error = new CredentialUnavailableError("The managed identity endpoint was reached, yet no tokens were received."); logger$e.getToken.info(formatError(scopes, error)); throw error; } // Since `authenticateManagedIdentity` didn't throw, and the result was not null, // We will assume that this endpoint is reachable from this point forward, // and avoid pinging again to it. this.isEndpointUnavailable = false; } else { // We've previously determined that the endpoint was unavailable, // either because it was unreachable or permanently unable to authenticate. const error = new CredentialUnavailableError("The managed identity endpoint is not currently available"); logger$e.getToken.info(formatError(scopes, error)); throw error; } logger$e.getToken.info(formatSuccess(scopes)); return result; } catch (err) { // CredentialUnavailable errors are expected to reach here. // We intend them to bubble up, so that DefaultAzureCredential can catch them. if (err.name === "AuthenticationRequiredError") { throw err; } // Expected errors to reach this point: // - Errors coming from a method unexpectedly breaking. // - When identityClient.sendTokenRequest throws, in which case // if the status code was 400, it means that the endpoint is working, // but no identity is available. span.setStatus({ status: "error", error: err, }); // If either the network is unreachable, // we can safely assume the credential is unavailable. if (err.code === "ENETUNREACH") { const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. Network unreachable. Message: ${err.message}`); logger$e.getToken.info(formatError(scopes, error)); throw error; } // If either the host was unreachable, // we can safely assume the credential is unavailable. if (err.code === "EHOSTUNREACH") { const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. No managed identity endpoint found. Message: ${err.message}`); logger$e.getToken.info(formatError(scopes, error)); throw error; } // If err.statusCode has a value of 400, it comes from sendTokenRequest, // and it means that the endpoint is working, but that no identity is available. if (err.statusCode === 400) { throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: The managed identity endpoint is indicating there's no available identity. Message: ${err.message}`); } // This is a special case for Docker Desktop which responds with a 403 with a message that contains "A socket operation was attempted to an unreachable network" or "A socket operation was attempted to an unreachable host" // rather than just timing out, as expected. if (err.statusCode === 403 || err.code === 403) { if (err.message.includes("unreachable")) { const error = new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Unavailable. Network unreachable. Message: ${err.message}`); logger$e.getToken.info(formatError(scopes, error)); throw error; } } // If the error has no status code, we can assume there was no available identity. // This will throw silently during any ChainedTokenCredential. if (err.statusCode === undefined) { throw new CredentialUnavailableError(`${ManagedIdentityCredential.name}: Authentication failed. Message ${err.message}`); } // Any other error should break the chain. throw new AuthenticationError(err.statusCode, { error: `${ManagedIdentityCredential.name} authentication failed.`, error_description: err.message, }); } finally { // Finally is always called, both if we return and if we throw in the above try/catch. span.end(); } } /** * Handles the MSAL authentication result. * If the result has an account, we update the local account reference. * If the token received is invalid, an error will be thrown depending on what's missing. */ handleResult(scopes, result, getTokenOptions) { this.ensureValidMsalToken(scopes, result, getTokenOptions); logger$e.getToken.info(formatSuccess(scopes)); return { token: result.accessToken, expiresOnTimestamp: result.expiresOn.getTime(), }; } /** * Ensures the validity of the MSAL token */ ensureValidMsalToken(scopes, msalToken, getTokenOptions) { const error = (message) => { logger$e.getToken.info(message); return new AuthenticationRequiredError({ scopes: Array.isArray(scopes) ? scopes : [scopes], getTokenOptions, message, }); }; if (!msalToken) { throw error("No response"); } if (!msalToken.expiresOn) { throw error(`Response had no "expiresOn" property.`); } if (!msalToken.accessToken) { throw error(`Response had no "accessToken" property.`); } } initializeSetAppTokenProvider() { if (!this.isAppTokenProviderInitialized) { this.confidentialApp.SetAppTokenProvider(async (appTokenProviderParameters) => { logger$e.info(`SetAppTokenProvider invoked with parameters- ${JSON.stringify(appTokenProviderParameters)}`); const getTokenOptions = Object.assign({}, appTokenProviderParameters); logger$e.info(`authenticateManagedIdentity invoked with scopes- ${JSON.stringify(appTokenProviderParameters.scopes)} and getTokenOptions - ${JSON.stringify(getTokenOptions)}`); const resultToken = await this.authenticateManagedIdentity(appTokenProviderParameters.scopes, getTokenOptions); if (resultToken) { logger$e.info(`SetAppTokenProvider will save the token in cache`); const expiresInSeconds = (resultToken === null || resultToken === void 0 ? void 0 : resultToken.expiresOnTimestamp) ? Math.floor((resultToken.expiresOnTimestamp - Date.now()) / 1000) : 0; return { accessToken: resultToken === null || resultToken === void 0 ? void 0 : resultToken.token, expiresInSeconds, }; } else { logger$e.info(`SetAppTokenProvider token has "no_access_token_returned" as the saved token`); return { accessToken: "no_access_token_returned", expiresInSeconds: 0, }; } }); this.isAppTokenProviderInitialized = true; } } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Ensures the scopes value is an array. * @internal */ function ensureScopes(scopes) { return Array.isArray(scopes) ? scopes : [scopes]; } /** * Throws if the received scope is not valid. * @internal */ function ensureValidScopeForDevTimeCreds(scope, logger) { if (!scope.match(/^[0-9a-zA-Z-_.:/]+$/)) { const error = new Error("Invalid scope was specified by the user or calling client"); logger.getToken.info(formatError(scope, error)); throw error; } } /** * Returns the resource out of a scope. * @internal */ function getScopeResource(scope) { return scope.replace(/\/.default$/, ""); } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Mockable reference to the CLI credential cliCredentialFunctions * @internal */ const cliCredentialInternals = { /** * @internal */ getSafeWorkingDir() { if (process.platform === "win32") { if (!process.env.SystemRoot) { throw new Error("Azure CLI credential expects a 'SystemRoot' environment variable"); } return process.env.SystemRoot; } else { return "/bin"; } }, /** * Gets the access token from Azure CLI * @param resource - The resource to use when getting the token * @internal */ async getAzureCliAccessToken(resource, tenantId, timeout) { let tenantSection = []; if (tenantId) { tenantSection = ["--tenant", tenantId]; } return new Promise((resolve, reject) => { try { child_process.execFile("az", [ "account", "get-access-token", "--output", "json", "--resource", resource, ...tenantSection, ], { cwd: cliCredentialInternals.getSafeWorkingDir(), shell: true, timeout }, (error, stdout, stderr) => { resolve({ stdout: stdout, stderr: stderr, error }); }); } catch (err) { reject(err); } }); }, }; const logger$d = credentialLogger("AzureCliCredential"); /** * This credential will use the currently logged-in user login information * via the Azure CLI ('az') commandline tool. * To do so, it will read the user access token and expire time * with Azure CLI command "az account get-access-token". */ class AzureCliCredential { /** * Creates an instance of the {@link AzureCliCredential}. * * To use this credential, ensure that you have already logged * in via the 'az' tool using the command "az login" from the commandline. * * @param options - Options, to optionally allow multi-tenant requests. */ constructor(options) { if (options === null || options === void 0 ? void 0 : options.tenantId) { checkTenantId(logger$d, options === null || options === void 0 ? void 0 : options.tenantId); this.tenantId = options === null || options === void 0 ? void 0 : options.tenantId; } this.additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options === null || options === void 0 ? void 0 : options.additionallyAllowedTenants); this.timeout = options === null || options === void 0 ? void 0 : options.processTimeoutInMs; } /** * Authenticates with Microsoft Entra ID and returns an access token if successful. * If authentication fails, a {@link CredentialUnavailableError} will be thrown with the details of the failure. * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ async getToken(scopes, options = {}) { const tenantId = processMultiTenantRequest(this.tenantId, options, this.additionallyAllowedTenantIds); if (tenantId) { checkTenantId(logger$d, tenantId); } const scope = typeof scopes === "string" ? scopes : scopes[0]; logger$d.getToken.info(`Using the scope ${scope}`); return tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => { var _a, _b, _c, _d; try { ensureValidScopeForDevTimeCreds(scope, logger$d); const resource = getScopeResource(scope); const obj = await cliCredentialInternals.getAzureCliAccessToken(resource, tenantId, this.timeout); const specificScope = (_a = obj.stderr) === null || _a === void 0 ? void 0 : _a.match("(.*)az login --scope(.*)"); const isLoginError = ((_b = obj.stderr) === null || _b === void 0 ? void 0 : _b.match("(.*)az login(.*)")) && !specificScope; const isNotInstallError = ((_c = obj.stderr) === null || _c === void 0 ? void 0 : _c.match("az:(.*)not found")) || ((_d = obj.stderr) === null || _d === void 0 ? void 0 : _d.startsWith("'az' is not recognized")); if (isNotInstallError) { 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'."); logger$d.getToken.info(formatError(scopes, error)); throw error; } if (isLoginError) { const error = new CredentialUnavailableError("Please run 'az login' from a command prompt to authenticate before using this credential."); logger$d.getToken.info(formatError(scopes, error)); throw error; } try { const responseData = obj.stdout; const response = this.parseRawResponse(responseData); logger$d.getToken.info(formatSuccess(scopes)); return response; } catch (e) { if (obj.stderr) { throw new CredentialUnavailableError(obj.stderr); } throw e; } } catch (err) { const error = err.name === "CredentialUnavailableError" ? err : new CredentialUnavailableError(err.message || "Unknown error while trying to retrieve the access token"); logger$d.getToken.info(formatError(scopes, error)); throw error; } }); } /** * Parses the raw JSON response from the Azure CLI into a usable AccessToken object * * @param rawResponse - The raw JSON response from the Azure CLI * @returns An access token with the expiry time parsed from the raw response * * The expiryTime of the credential's access token, in milliseconds, is calculated as follows: * * When available, expires_on (introduced in Azure CLI v2.54.0) will be preferred. Otherwise falls back to expiresOn. */ parseRawResponse(rawResponse) { const response = JSON.parse(rawResponse); const token = response.accessToken; // if available, expires_on will be a number representing seconds since epoch. // ensure it's a number or NaN let expiresOnTimestamp = Number.parseInt(response.expires_on, 10) * 1000; if (!isNaN(expiresOnTimestamp)) { logger$d.getToken.info("expires_on is available and is valid, using it"); return { token, expiresOnTimestamp, }; } // fallback to the older expiresOn - an RFC3339 date string expiresOnTimestamp = new Date(response.expiresOn).getTime(); // ensure expiresOn is well-formatted if (isNaN(expiresOnTimestamp)) { throw new CredentialUnavailableError(`Unexpected response from Azure CLI when getting token. Expected "expiresOn" to be a RFC3339 date string. Got: "${response.expiresOn}"`); } return { token, expiresOnTimestamp, }; } } // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Mockable reference to the Developer CLI credential cliCredentialFunctions * @internal */ const developerCliCredentialInternals = { /** * @internal */ getSafeWorkingDir() { if (process.platform === "win32") { if (!process.env.SystemRoot) { throw new Error("Azure Developer CLI credential expects a 'SystemRoot' environment variable"); } return process.env.SystemRoot; } else { return "/bin"; } }, /** * Gets the access token from Azure Developer CLI * @param scopes - The scopes to use when getting the token * @internal */ async getAzdAccessToken(scopes, tenantId, timeout) { let tenantSection = []; if (tenantId) { tenantSection = ["--tenant-id", tenantId]; } return new Promise((resolve, reject) => { try { child_process.execFile("azd", [ "auth", "token", "--output", "json", ...scopes.reduce((previous, current) => previous.concat("--scope", current), []), ...tenantSection, ], { cwd: developerCliCredentialInternals.getSafeWorkingDir(), timeout, }, (error, stdout, stderr) => { resolve({ stdout, stderr, error }); }); } catch (err) { reject(err); } }); }, }; const logger$c = credentialLogger("AzureDeveloperCliCredential"); /** * Azure Developer CLI is a command-line interface tool that allows developers to create, manage, and deploy * resources in Azure. It's built on top of the Azure CLI and provides additional functionality specific * to Azure developers. It allows users to authenticate as a user and/or a service principal against * Microsoft Entra ID. The * AzureDeveloperCliCredential authenticates in a development environment and acquires a token on behalf of * the logged-in user or service principal in the Azure Developer CLI. It acts as the Azure Developer CLI logged in user or * service principal and executes an Azure CLI command underneath to authenticate the application against * Microsoft Entra ID. * *