fix MCP auth token refresh and disable legacy SSE
This commit is contained in:
90
mcp-server/auth/sessionRefresh.js
Normal file
90
mcp-server/auth/sessionRefresh.js
Normal file
@@ -0,0 +1,90 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user