@azure/identity
Version:
Provides credential implementations for Azure SDK libraries that can authenticate with Microsoft Entra ID
239 lines (238 loc) • 9.69 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var azureCliCredential_exports = {};
__export(azureCliCredential_exports, {
AzureCliCredential: () => AzureCliCredential,
azureCliPublicErrorMessages: () => azureCliPublicErrorMessages,
cliCredentialInternals: () => cliCredentialInternals
});
module.exports = __toCommonJS(azureCliCredential_exports);
var import_tenantIdUtils = require("../util/tenantIdUtils.js");
var import_logging = require("../util/logging.js");
var import_scopeUtils = require("../util/scopeUtils.js");
var import_errors = require("../errors.js");
var import_tracing = require("../util/tracing.js");
var import_subscriptionUtils = require("../util/subscriptionUtils.js");
var import_processUtils = require("../util/processUtils.js");
var import_core_process = require("@azure/core-process");
const logger = (0, import_logging.credentialLogger)("AzureCliCredential");
const azureCliPublicErrorMessages = {
claim: "This credential doesn't support claims challenges. To authenticate with the required claims, please run the following command:",
notInstalled: "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'.",
login: "Please run 'az login' from a command prompt to authenticate before using this credential.",
unknown: "Unknown error while trying to retrieve the access token",
unexpectedResponse: 'Unexpected response from Azure CLI when getting token. Expected "expiresOn" to be a RFC3339 date string. Got:'
};
const cliCredentialInternals = {
/**
* @internal
*/
getSafeWorkingDir() {
if (process.platform === "win32") {
let systemRoot = process.env.SystemRoot || process.env["SYSTEMROOT"];
if (!systemRoot) {
logger.getToken.warning(
"The SystemRoot environment variable is not set. This may cause issues when using the Azure CLI credential."
);
systemRoot = "C:\\Windows";
}
return 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, subscription, timeout) {
let tenantSection = [];
let subscriptionSection = [];
if (tenantId) {
tenantSection = ["--tenant", tenantId];
}
if (subscription) {
subscriptionSection = ["--subscription", subscription];
}
const args = [
"account",
"get-access-token",
"--output",
"json",
"--resource",
resource,
...tenantSection,
...subscriptionSection
];
return import_processUtils.processUtils.execFileWithResult("az", args, {
allowWindowsBatchFiles: true,
cwd: cliCredentialInternals.getSafeWorkingDir(),
encoding: "utf8",
timeout
});
}
};
class AzureCliCredential {
tenantId;
additionallyAllowedTenantIds;
timeout;
subscription;
/**
* 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?.tenantId) {
(0, import_tenantIdUtils.checkTenantId)(logger, options?.tenantId);
this.tenantId = options?.tenantId;
}
if (options?.subscription) {
(0, import_subscriptionUtils.checkSubscription)(logger, options?.subscription);
this.subscription = options?.subscription;
}
this.additionallyAllowedTenantIds = (0, import_tenantIdUtils.resolveAdditionallyAllowedTenantIds)(
options?.additionallyAllowedTenants
);
this.timeout = 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 scope = typeof scopes === "string" ? scopes : scopes[0];
const claimsValue = options.claims;
if (claimsValue && claimsValue.trim()) {
const encodedClaims = btoa(claimsValue);
let loginCmd = `az login --claims-challenge ${encodedClaims} --scope ${scope}`;
const tenantIdFromOptions = options.tenantId;
if (tenantIdFromOptions) {
loginCmd += ` --tenant ${tenantIdFromOptions}`;
}
const error = new import_errors.CredentialUnavailableError(
`${azureCliPublicErrorMessages.claim} ${loginCmd}`
);
logger.getToken.info((0, import_logging.formatError)(scope, error));
throw error;
}
const tenantId = (0, import_tenantIdUtils.processMultiTenantRequest)(
this.tenantId,
options,
this.additionallyAllowedTenantIds
);
if (tenantId) {
(0, import_tenantIdUtils.checkTenantId)(logger, tenantId);
}
if (this.subscription) {
(0, import_subscriptionUtils.checkSubscription)(logger, this.subscription);
}
logger.getToken.info(`Using the scope ${scope}`);
return import_tracing.tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => {
try {
(0, import_scopeUtils.ensureValidScopeForDevTimeCreds)(scope, logger);
const resource = (0, import_scopeUtils.getScopeResource)(scope);
const obj = await cliCredentialInternals.getAzureCliAccessToken(
resource,
tenantId,
this.subscription,
this.timeout
);
const specificScope = obj.stderr?.match("(.*)az login --scope(.*)");
const isLoginError = obj.stderr?.match("(.*)az login(.*)") && !specificScope;
const isNotInstallError = obj.stderr?.match("az:(.*)not found") || obj.stderr?.startsWith("'az' is not recognized") || obj.error && (0, import_core_process.isProcessError)(obj.error) && obj.error.code === "ENOENT";
if (isNotInstallError) {
const error = new import_errors.CredentialUnavailableError(azureCliPublicErrorMessages.notInstalled);
logger.getToken.info((0, import_logging.formatError)(scopes, error));
throw error;
}
if (isLoginError) {
const error = new import_errors.CredentialUnavailableError(azureCliPublicErrorMessages.login);
logger.getToken.info((0, import_logging.formatError)(scopes, error));
throw error;
}
try {
const responseData = obj.stdout;
const response = this.parseRawResponse(responseData);
logger.getToken.info((0, import_logging.formatSuccess)(scopes));
return response;
} catch (e) {
if (obj.stderr) {
throw new import_errors.CredentialUnavailableError(obj.stderr);
}
throw e;
}
} catch (err) {
const error = err.name === "CredentialUnavailableError" ? err : new import_errors.CredentialUnavailableError(
err.message || azureCliPublicErrorMessages.unknown
);
logger.getToken.info((0, import_logging.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;
let expiresOnTimestamp = Number.parseInt(response.expires_on, 10) * 1e3;
if (!isNaN(expiresOnTimestamp)) {
logger.getToken.info("expires_on is available and is valid, using it");
return {
token,
expiresOnTimestamp,
tokenType: "Bearer"
};
}
expiresOnTimestamp = new Date(response.expiresOn).getTime();
if (isNaN(expiresOnTimestamp)) {
throw new import_errors.CredentialUnavailableError(
`${azureCliPublicErrorMessages.unexpectedResponse} "${response.expiresOn}"`
);
}
return {
token,
expiresOnTimestamp,
tokenType: "Bearer"
};
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AzureCliCredential,
azureCliPublicErrorMessages,
cliCredentialInternals
});
//# sourceMappingURL=azureCliCredential.js.map