armpit
Version:
Another resource manager programming interface toolkit.
97 lines • 3.92 kB
JavaScript
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