UNPKG

@azure/identity

Version:

Provides credential implementations for Azure SDK libraries that can authenticate with Microsoft Entra ID

189 lines (188 loc) • 9.17 kB
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