Files
agenticSystem/mcp-server/auth/sessionRefresh.js
2026-07-18 10:03:12 +00:00

91 lines
3.0 KiB
JavaScript

import { fetchProjectInfo } from "./localClient.js";
import {
getSessionCredentials,
sessionCredentials,
setMcpSessionCredentials,
} from "./credentials.js";
const refreshInFlight = new Map();
export function isJwtExpiring(token, marginSeconds = 300) {
try {
const payload = token.split(".")[1];
if (!payload) return true;
const data = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
return !data.exp || Math.floor(Date.now() / 1000) >= data.exp - marginSeconds;
} catch {
return true;
}
}
export async function refreshSessionCredentials(
sessionId,
{ force = false, fetcher = fetchProjectInfo } = {},
) {
const current = await getSessionCredentials(sessionId);
if (!current?.project_dir) {
throw new Error("Project dir no disponible en esta sesion");
}
if (force && !current.mcp_secret && !process.env.ACAI_AUTH_HEADER) {
throw new Error("No hay autenticacion interna para autorizar la renovacion forzada");
}
const key = current.project_dir;
const existing = refreshInFlight.get(key);
if (existing) return existing;
const operation = (async () => {
let info;
try {
info = await fetcher(
{ project_dir: current.project_dir },
current.acai_user || null,
{
forceTokenRefresh: force,
mcpSecret: force ? current.mcp_secret : null,
},
);
} catch (error) {
throw new Error(error.response?.data?.error || error.message);
}
if (!info?.success) {
throw new Error(info?.error || "No se pudieron renovar las credenciales Acai");
}
if (!info.token || isJwtExpiring(info.token, 0)) {
throw new Error("El backend no devolvio un token Acai valido");
}
const fresh = {
...current,
token: info.token,
tokenHash: info.tokenHash || "",
website: info.domain || current.website,
web_url: info.web_url || current.web_url,
api_web_url: info.api_web_url || info.web_url || current.api_web_url,
forge_host: info.forge_host ?? current.forge_host,
project_dir: info.project_dir || current.project_dir,
mode: info.mode || current.mode,
};
sessionCredentials.set(sessionId, fresh);
setMcpSessionCredentials(sessionId, fresh);
return fresh;
})();
refreshInFlight.set(key, operation);
try {
return await operation;
} finally {
if (refreshInFlight.get(key) === operation) {
refreshInFlight.delete(key);
}
}
}
export async function ensureFreshSessionCredentials(sessionId) {
const current = await getSessionCredentials(sessionId);
if (!current?.token || !current.project_dir || !isJwtExpiring(current.token)) {
return current;
}
return refreshSessionCredentials(sessionId);
}