@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
125 lines (124 loc) • 5.87 kB
JavaScript
import { execFileSync } from "node:child_process";
import { logSuccess, logWarn } from "./log.js";
export async function resolveTemporalAuth(tenant, env, platformName = tenant) {
const { SSMClient, GetParameterCommand } = await import("@aws-sdk/client-ssm");
const ssm = new SSMClient({ region: process.env.AWS_REGION || "us-east-2" });
const results = {};
async function trySSM(name) {
try {
const resp = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true }));
return resp.Parameter?.Value ?? undefined;
}
catch {
return undefined;
}
}
try {
const workerPath = `/mesh-platform/${tenant}/${env}/temporal/worker-service-account-key`;
const exchangePath = `/mesh-platform/${tenant}/${env}/temporal/token-exchange-app`;
const workerJson = await trySSM(workerPath);
if (workerJson) {
const worker = JSON.parse(workerJson);
results.TEMPORAL_AUTH_CLIENT_ID = worker.clientId;
results.TEMPORAL_AUTH_CLIENT_SECRET = worker.clientSecret;
}
const exchangeJson = await trySSM(exchangePath);
if (exchangeJson) {
const exchange = JSON.parse(exchangeJson);
results.ZITADEL_TOKEN_EXCHANGE_CLIENT_ID = exchange.clientId;
results.ZITADEL_TOKEN_EXCHANGE_CLIENT_SECRET = exchange.clientSecret;
}
const platformJson = await trySSM(`/mesh-platform/${platformName}/${env}/platform/zitadel`);
if (platformJson) {
const platform = JSON.parse(platformJson);
if (platform.endpoint)
results.ZITADEL_ISSUER = platform.endpoint;
if (platform.projectId)
results.ZITADEL_PROJECT_ID = platform.projectId;
}
if (!results.TEMPORAL_AUTH_CLIENT_ID) {
const legacyClientId = await trySSM("/platform/temporal/auth/CLIENT_ID");
const legacyClientSecret = await trySSM("/platform/temporal/auth/CLIENT_SECRET");
if (legacyClientId && legacyClientSecret) {
results.TEMPORAL_AUTH_CLIENT_ID = legacyClientId;
results.TEMPORAL_AUTH_CLIENT_SECRET = legacyClientSecret;
}
}
if (!results.ZITADEL_ISSUER) {
try {
const podEnv = execFileSync("kubectl", [
"get",
"pods",
"-n",
`temporal-${env}`,
"-l",
"app.kubernetes.io/component=frontend",
"-o",
"jsonpath={.items[0].spec.containers[0].env}",
], { encoding: "utf-8", timeout: 10_000, stdio: ["pipe", "pipe", "pipe"] }).trim();
if (podEnv) {
const envVars = JSON.parse(podEnv);
for (const v of envVars) {
if (v.name === "ZITADEL_ISSUER_URL" && v.value && !results.ZITADEL_ISSUER) {
results.ZITADEL_ISSUER = v.value;
}
if (v.name === "ZITADEL_PROJECT_ID" && v.value && !results.ZITADEL_PROJECT_ID) {
results.ZITADEL_PROJECT_ID = v.value;
}
}
}
}
catch (err) {
const detail = err instanceof Error ? err.message : String(err);
const stderr = String(err?.stderr ?? "");
if (/\.kube[/\\]config|error loading config file/i.test(`${detail} ${stderr}`)) {
logWarn("kubectl could not read a kubeconfig and fell back to ~/.kube/config " +
"(macOS blocks it for shells without Full Disk Access). Run inside the " +
"mesh dev tmux session, or export KUBECONFIG=$TMPDIR/mesh-dev-sessions/<session>.kubeconfig.");
}
}
}
if (results.TEMPORAL_AUTH_CLIENT_ID && results.ZITADEL_ISSUER) {
logSuccess(`Temporal auth resolved (client: ${results.TEMPORAL_AUTH_CLIENT_ID})`);
}
else if (Object.keys(results).length > 0) {
logWarn("Partial Temporal auth resolved (some credentials missing)");
}
}
catch (err) {
logWarn(`Could not resolve Temporal auth from SSM: ${err instanceof Error ? err.message : String(err)}`);
}
return results;
}
export async function acquireTemporalBearer(vars = {}) {
const issuerUrl = vars.ZITADEL_ISSUER ?? process.env.ZITADEL_ISSUER;
const projectId = vars.ZITADEL_PROJECT_ID ?? process.env.ZITADEL_PROJECT_ID;
const clientId = vars.TEMPORAL_AUTH_CLIENT_ID ?? process.env.TEMPORAL_AUTH_CLIENT_ID;
const clientSecret = vars.TEMPORAL_AUTH_CLIENT_SECRET ?? process.env.TEMPORAL_AUTH_CLIENT_SECRET;
if (!issuerUrl || !clientId || !clientSecret) {
return undefined;
}
const scopes = ["openid"];
if (projectId) {
scopes.push(`urn:zitadel:iam:org:project:id:${projectId}:aud`);
if (process.env.TEMPORAL_M2M_SKIP_ROLES_SCOPE !== "true") {
scopes.push("urn:zitadel:iam:org:projects:roles");
}
}
const response = await fetch(`${issuerUrl}/oauth/v2/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
scope: scopes.join(" "),
}),
});
if (!response.ok) {
const text = await response.text();
throw new Error(`Temporal token acquisition failed: ${response.status} ${response.statusText} — ${text}`);
}
const data = (await response.json());
return data.access_token;
}