UNPKG

@azure/identity

Version:

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

286 lines (278 loc) • 11.6 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 azurePowerShellCredential_exports = {}; __export(azurePowerShellCredential_exports, { AzurePowerShellCredential: () => AzurePowerShellCredential, commandStack: () => commandStack, createPowerShellEnvironment: () => createPowerShellEnvironment, formatCommand: () => formatCommand, parseJsonToken: () => parseJsonToken, powerShellErrors: () => powerShellErrors, powerShellPublicErrorMessages: () => powerShellPublicErrorMessages }); module.exports = __toCommonJS(azurePowerShellCredential_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_processUtils = require("../util/processUtils.js"); var import_tracing = require("../util/tracing.js"); const logger = (0, import_logging.credentialLogger)("AzurePowerShellCredential"); const isWindows = process.platform === "win32"; const powerShellResourceEnvironmentVariable = "AZURE_IDENTITY_POWERSHELL_RESOURCE"; const powerShellTenantEnvironmentVariable = "AZURE_IDENTITY_POWERSHELL_TENANT_ID"; const powerShellPrivateEnvironmentVariables = new Set( [powerShellResourceEnvironmentVariable, powerShellTenantEnvironmentVariable].map( (name) => name.toLowerCase() ) ); function createPowerShellEnvironment(resource, tenantId, environment = process.env) { const childEnvironment = Object.fromEntries( Object.entries(environment).filter( ([name]) => !powerShellPrivateEnvironmentVariables.has(name.toLowerCase()) ) ); childEnvironment[powerShellResourceEnvironmentVariable] = resource; childEnvironment[powerShellTenantEnvironmentVariable] = tenantId ?? ""; return childEnvironment; } function formatCommand(commandName) { if (isWindows) { return `${commandName}.exe`; } else { return commandName; } } async function runCommands(commands, timeout, env) { const results = []; for (const command of commands) { const [file, ...parameters] = command; const result = await import_processUtils.processUtils.execFile(file, parameters, { encoding: "utf8", env, timeout }); results.push(result); } return results; } const powerShellErrors = { login: "Run Connect-AzAccount to login", installed: "The specified module 'Az.Accounts' with version '2.2.0' was not loaded because no valid module file was found in any module directory" }; const powerShellPublicErrorMessages = { login: "Please run 'Connect-AzAccount' from PowerShell to authenticate before using this credential.", installed: `The 'Az.Account' module >= 2.2.0 is not installed. Install the Azure Az PowerShell module with: "Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force".`, claim: "This credential doesn't support claims challenges. To authenticate with the required claims, please run the following command:", troubleshoot: `To troubleshoot, visit https://aka.ms/azsdk/js/identity/powershellcredential/troubleshoot.` }; const isLoginError = (err) => err.message.match(`(.*)${powerShellErrors.login}(.*)`); const isNotInstalledError = (err) => err.message.match(powerShellErrors.installed); const commandStack = [formatCommand("pwsh")]; if (isWindows) { commandStack.push(formatCommand("powershell")); } class AzurePowerShellCredential { tenantId; additionallyAllowedTenantIds; timeout; /** * Creates an instance of the {@link AzurePowerShellCredential}. * * To use this credential: * - Install the Azure Az PowerShell module with: * `Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force`. * - You have already logged in to Azure PowerShell using the command * `Connect-AzAccount` from the command line. * * @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; } this.additionallyAllowedTenantIds = (0, import_tenantIdUtils.resolveAdditionallyAllowedTenantIds)( options?.additionallyAllowedTenants ); this.timeout = options?.processTimeoutInMs; } /** * Gets the access token from Azure PowerShell * @param resource - The resource to use when getting the token */ async getAzurePowerShellAccessToken(resource, tenantId, timeout) { for (const powerShellCommand of [...commandStack]) { try { await runCommands([[powerShellCommand, "/?"]], timeout); } catch (e) { commandStack.shift(); continue; } const results = await runCommands( [ [ powerShellCommand, "-NoProfile", "-NonInteractive", "-Command", ` $tenantId = $env:${powerShellTenantEnvironmentVariable} $resource = $env:${powerShellResourceEnvironmentVariable} $m = Import-Module Az.Accounts -MinimumVersion 2.2.0 -PassThru $useSecureString = $m.Version -ge [version]'2.17.0' -and $m.Version -lt [version]'5.0.0' $params = @{ ResourceUrl = $resource } if ($tenantId.Length -gt 0) { $params["TenantId"] = $tenantId } if ($useSecureString) { $params["AsSecureString"] = $true } $token = Get-AzAccessToken @params $result = New-Object -TypeName PSObject $result | Add-Member -MemberType NoteProperty -Name ExpiresOn -Value $token.ExpiresOn if ($token.Token -is [System.Security.SecureString]) { if ($PSVersionTable.PSVersion.Major -lt 7) { $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($token.Token) try { $result | Add-Member -MemberType NoteProperty -Name Token -Value ([System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)) } finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr) } } else { $result | Add-Member -MemberType NoteProperty -Name Token -Value ($token.Token | ConvertFrom-SecureString -AsPlainText) } } else { $result | Add-Member -MemberType NoteProperty -Name Token -Value $token.Token } Write-Output (ConvertTo-Json $result) ` ] ], timeout, createPowerShellEnvironment(resource, tenantId) ); const result = results[0]; return parseJsonToken(result); } throw new Error(`Unable to execute PowerShell. Ensure that it is installed in your system`); } /** * Authenticates with Microsoft Entra ID and returns an access token if successful. * If the authentication cannot be performed through PowerShell, a {@link CredentialUnavailableError} will be thrown. * * @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 import_tracing.tracingClient.withSpan(`${this.constructor.name}.getToken`, options, async () => { const scope = typeof scopes === "string" ? scopes : scopes[0]; const claimsValue = options.claims; if (claimsValue && claimsValue.trim()) { const encodedClaims = btoa(claimsValue); let loginCmd = `Connect-AzAccount -ClaimsChallenge ${encodedClaims}`; const tenantIdFromOptions = options.tenantId; if (tenantIdFromOptions) { loginCmd += ` -Tenant ${tenantIdFromOptions}`; } const error = new import_errors.CredentialUnavailableError( `${powerShellPublicErrorMessages.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); } try { (0, import_scopeUtils.ensureValidScopeForDevTimeCreds)(scope, logger); logger.getToken.info(`Using the scope ${scope}`); const resource = (0, import_scopeUtils.getScopeResource)(scope); const response = await this.getAzurePowerShellAccessToken(resource, tenantId, this.timeout); logger.getToken.info((0, import_logging.formatSuccess)(scopes)); return { token: response.Token, expiresOnTimestamp: new Date(response.ExpiresOn).getTime(), tokenType: "Bearer" }; } catch (err) { if (isNotInstalledError(err)) { const error2 = new import_errors.CredentialUnavailableError(powerShellPublicErrorMessages.installed); logger.getToken.info((0, import_logging.formatError)(scope, error2)); throw error2; } else if (isLoginError(err)) { const error2 = new import_errors.CredentialUnavailableError(powerShellPublicErrorMessages.login); logger.getToken.info((0, import_logging.formatError)(scope, error2)); throw error2; } const error = new import_errors.CredentialUnavailableError( `${err}. ${powerShellPublicErrorMessages.troubleshoot}` ); logger.getToken.info((0, import_logging.formatError)(scope, error)); throw error; } }); } } async function parseJsonToken(result) { const jsonRegex = /{[^{}]*}/g; const matches = result.match(jsonRegex); let resultWithoutToken = result; if (matches) { try { for (const item of matches) { try { const jsonContent = JSON.parse(item); if (jsonContent?.Token) { resultWithoutToken = resultWithoutToken.replace(item, ""); if (resultWithoutToken) { logger.getToken.warning(resultWithoutToken); } return jsonContent; } } catch (e) { continue; } } } catch (e) { throw new Error(`Unable to parse the output of PowerShell. Received output: ${result}`); } } throw new Error(`No access token found in the output. Received output: ${result}`); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AzurePowerShellCredential, commandStack, createPowerShellEnvironment, formatCommand, parseJsonToken, powerShellErrors, powerShellPublicErrorMessages }); //# sourceMappingURL=azurePowerShellCredential.js.map