UNPKG

armpit

Version:

Another resource manager programming interface toolkit.

97 lines 3.92 kB
import { isAbortSignal } from "./tsUtils.js"; import { isTenantId, isSubscriptionId, isSubscriptionIdOrName, isScope, } from "./azureUtils.js"; export function buildCliCredential(invoker, options) { const defaultTenantId = options?.tenantId; if (defaultTenantId && !isTenantId(defaultTenantId)) { throw new Error("Invalid tenant ID."); } const defaultSubscription = options?.subscription; if (defaultSubscription && !isSubscriptionIdOrName(defaultSubscription)) { throw new Error("Invalid subscription name or subscription ID."); } let lastTokenContext = null; const getToken = async function (scopes, options) { // This is loosely based on AzureCliCredential but uses the internals provided by this library if (typeof scopes === "string") { scopes = [scopes]; } if (!scopes.every(isScope)) { throw new Error("Scopes are invalid"); } const args = ["--scope", ...scopes]; const tenantId = options?.tenantId ?? defaultTenantId; if (tenantId) { args.push("--tenant", tenantId); } else if (defaultSubscription) { args.push("--subscription", defaultSubscription); } const invokerFn = isAbortSignal(options?.abortSignal) ? invoker({ abortSignal: options.abortSignal }) : invoker; const result = await invokerFn `account get-access-token ${args}`; let expiresOnMs = null; if (result.expires_on != null) { if (typeof result.expires_on === "number") { expiresOnMs = result.expires_on * 1000; } else { expiresOnMs = Number.parseInt(result.expires_on, 10) * 1000; } } if (expiresOnMs == null || isNaN(expiresOnMs)) { expiresOnMs = new Date(result.expiresOn).getTime(); } if (expiresOnMs == null || isNaN(expiresOnMs)) { throw new Error("Failed to extract token expiration"); } const tokenType = result.tokenType ?? "Bearer"; if (tokenType !== "Bearer") { throw new Error(`Token type ${tokenType} is not supported`); } lastTokenContext = {}; if (isSubscriptionId(result.subscription)) { lastTokenContext.subscriptionId = result.subscription; } if (isTenantId(result.tenant)) { lastTokenContext.tenantId = result.tenant; } return { token: result.accessToken, expiresOnTimestamp: expiresOnMs, tokenType, }; }; const getLastTokenContext = function () { return lastTokenContext; }; // A plain object is returned so libraries that clone the credential object like tedious/mssql can be compatible. return { getToken, getLastTokenContext, }; } export class ArmpitCliCredentialFactory { #cache = []; #defaultInvoker; constructor(defaultInvoker) { this.#defaultInvoker = defaultInvoker ?? null; } getCredential(options, invokerOverride) { const invoker = invokerOverride ?? this.#defaultInvoker; if (invoker == null) { throw new Error("An invoker is required to build a credential"); } const cacheKeyValue = options != null && (options.subscription != null || options.tenantId != null) ? JSON.stringify(options) : null; if (cacheKeyValue) { const matching = this.#cache.find(e => e.options && JSON.stringify(e.options) === cacheKeyValue); if (matching) { return matching.credential; } } const credential = buildCliCredential(invoker, options); if (cacheKeyValue && options) { this.#cache.push({ options, credential }); } return credential; } } //# sourceMappingURL=armpitCredential.js.map