@azure/identity
Version:
Provides credential implementations for Azure SDK libraries that can authenticate with Microsoft Entra ID
189 lines (188 loc) • 9.17 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 azurePipelinesCredential_exports = {};
__export(azurePipelinesCredential_exports, {
AzurePipelinesCredential: () => AzurePipelinesCredential,
handleOidcResponse: () => handleOidcResponse
});
module.exports = __toCommonJS(azurePipelinesCredential_exports);
var import_errors = require("../errors.js");
var import_core_rest_pipeline = require("@azure/core-rest-pipeline");
var import_clientAssertionCredential = require("./clientAssertionCredential.js");
var import_identityClient = require("../client/identityClient.js");
var import_tenantIdUtils = require("../util/tenantIdUtils.js");
var import_logging = require("../util/logging.js");
const credentialName = "AzurePipelinesCredential";
const logger = (0, import_logging.credentialLogger)(credentialName);
const OIDC_API_VERSION = "7.1";
class AzurePipelinesCredential {
clientAssertionCredential;
identityClient;
/**
* AzurePipelinesCredential supports Federated Identity on Azure Pipelines through Service Connections.
* @param tenantId - tenantId associated with the service connection
* @param clientId - clientId associated with the service connection
* @param serviceConnectionId - Unique ID for the service connection, as found in the querystring's resourceId key
* @param systemAccessToken - The pipeline's <see href="https://learn.microsoft.com/azure/devops/pipelines/build/variables?view=azure-devops%26tabs=yaml#systemaccesstoken">System.AccessToken</see> value.
* @param options - The identity client options to use for authentication.
*/
constructor(tenantId, clientId, serviceConnectionId, systemAccessToken, options = {}) {
if (!clientId) {
throw new import_errors.CredentialUnavailableError(
`${credentialName}: is unavailable. clientId is a required parameter.`
);
}
if (!tenantId) {
throw new import_errors.CredentialUnavailableError(
`${credentialName}: is unavailable. tenantId is a required parameter.`
);
}
if (!serviceConnectionId) {
throw new import_errors.CredentialUnavailableError(
`${credentialName}: is unavailable. serviceConnectionId is a required parameter.`
);
}
if (!systemAccessToken) {
throw new import_errors.CredentialUnavailableError(
`${credentialName}: is unavailable. systemAccessToken is a required parameter.`
);
}
options.loggingOptions = {
...options?.loggingOptions,
additionalAllowedHeaderNames: [
...options.loggingOptions?.additionalAllowedHeaderNames ?? [],
"x-vss-e2eid",
"x-msedge-ref"
]
};
this.identityClient = new import_identityClient.IdentityClient(options);
(0, import_tenantIdUtils.checkTenantId)(logger, tenantId);
logger.info(
`Invoking AzurePipelinesCredential with tenant ID: ${tenantId}, client ID: ${clientId}, and service connection ID: ${serviceConnectionId}`
);
if (!process.env.SYSTEM_OIDCREQUESTURI) {
throw new import_errors.CredentialUnavailableError(
`${credentialName}: is unavailable. Ensure that you're running this task in an Azure Pipeline, so that following missing system variable(s) can be defined- "SYSTEM_OIDCREQUESTURI"`
);
}
const oidcRequestUrl = `${process.env.SYSTEM_OIDCREQUESTURI}?api-version=${OIDC_API_VERSION}&serviceConnectionId=${serviceConnectionId}`;
logger.info(
`Invoking ClientAssertionCredential with tenant ID: ${tenantId}, client ID: ${clientId} and service connection ID: ${serviceConnectionId}`
);
this.clientAssertionCredential = new import_clientAssertionCredential.ClientAssertionCredential(
tenantId,
clientId,
this.requestOidcToken.bind(this, oidcRequestUrl, systemAccessToken),
options
);
}
/**
* Authenticates with Microsoft Entra ID and returns an access token if successful.
* If authentication fails, a {@link CredentialUnavailableError} or {@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) {
if (!this.clientAssertionCredential) {
const errorMessage = `${credentialName}: is unavailable. To use Federation Identity in Azure Pipelines, the following parameters are required -
tenantId,
clientId,
serviceConnectionId,
systemAccessToken,
"SYSTEM_OIDCREQUESTURI".
See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`;
logger.error(errorMessage);
throw new import_errors.CredentialUnavailableError(errorMessage);
}
logger.info("Invoking getToken() of Client Assertion Credential");
return this.clientAssertionCredential.getToken(scopes, options);
}
/**
*
* @param oidcRequestUrl - oidc request url
* @param systemAccessToken - system access token
* @returns OIDC token from Azure Pipelines
*/
async requestOidcToken(oidcRequestUrl, systemAccessToken) {
logger.info("Requesting OIDC token from Azure Pipelines...");
logger.info(oidcRequestUrl);
const request = (0, import_core_rest_pipeline.createPipelineRequest)({
url: oidcRequestUrl,
method: "POST",
headers: (0, import_core_rest_pipeline.createHttpHeaders)({
"Content-Type": "application/json",
Authorization: `Bearer ${systemAccessToken}`,
// Prevents the service from responding with a redirect HTTP status code (useful for automation).
"X-TFS-FedAuthRedirect": "Suppress"
})
});
const response = await this.identityClient.sendRequest(request);
return handleOidcResponse(response);
}
}
function handleOidcResponse(response) {
const text = response.bodyAsText;
if (!text) {
logger.error(
`${credentialName}: Authentication Failed. Received null token from OIDC request. Response status- ${response.status}. Complete response - ${JSON.stringify(response)}`
);
throw new import_errors.AuthenticationError(response.status, {
error: `${credentialName}: Authentication Failed. Received null token from OIDC request.`,
error_description: `${JSON.stringify(
response
)}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`
});
}
try {
const result = JSON.parse(text);
if (result?.oidcToken) {
return result.oidcToken;
} else {
const errorMessage = `${credentialName}: Authentication Failed. oidcToken field not detected in the response.`;
let errorDescription = ``;
if (response.status !== 200) {
errorDescription = `Response body = ${text}. Response Headers ["x-vss-e2eid"] = ${response.headers.get("x-vss-e2eid")} and ["x-msedge-ref"] = ${response.headers.get("x-msedge-ref")}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`;
}
logger.error(errorMessage);
logger.error(errorDescription);
throw new import_errors.AuthenticationError(response.status, {
error: errorMessage,
error_description: errorDescription
});
}
} catch (e) {
const errorDetails = `${credentialName}: Authentication Failed. oidcToken field not detected in the response.`;
logger.error(
`Response from service = ${text}, Response Headers ["x-vss-e2eid"] = ${response.headers.get("x-vss-e2eid")}
and ["x-msedge-ref"] = ${response.headers.get("x-msedge-ref")}, error message = ${e.message}`
);
logger.error(errorDetails);
throw new import_errors.AuthenticationError(response.status, {
error: errorDetails,
error_description: `Response = ${text}. Response headers ["x-vss-e2eid"] = ${response.headers.get("x-vss-e2eid")} and ["x-msedge-ref"] = ${response.headers.get("x-msedge-ref")}. See the troubleshooting guide for more information: https://aka.ms/azsdk/js/identity/azurepipelinescredential/troubleshoot`
});
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AzurePipelinesCredential,
handleOidcResponse
});
//# sourceMappingURL=azurePipelinesCredential.js.map