fix MCP auth token refresh and disable legacy SSE
This commit is contained in:
@@ -2,8 +2,30 @@ import axios from "axios";
|
||||
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { sessionApiClients, getSessionCredentials, setCredentials, findRoleByToken } from "./credentials.js";
|
||||
import { assertSafeCmsTarget } from "../utils/cmsTargetSafety.js";
|
||||
import { ensureFreshSessionCredentials, refreshSessionCredentials } from "./sessionRefresh.js";
|
||||
const DEFAULT_ROLE = 'developer';
|
||||
|
||||
export const isAcaiTokenFailure = (error) => {
|
||||
const status = error?.response?.status;
|
||||
const responseText = JSON.stringify(error?.response?.data || "");
|
||||
return status === 401
|
||||
|| (status === 403 && /token|jwt|auth|unauthoriz|expired|no v[aá]lido/i.test(responseText));
|
||||
};
|
||||
|
||||
export const runWithTokenRefreshRetry = async (
|
||||
sessionId,
|
||||
operation,
|
||||
{ refreshCredentials = refreshSessionCredentials } = {},
|
||||
) => {
|
||||
try {
|
||||
return await operation();
|
||||
} catch (error) {
|
||||
if (!isAcaiTokenFailure(error) || error?.config?._acaiTokenRetry) throw error;
|
||||
await refreshCredentials(sessionId, { force: true });
|
||||
return operation();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if session is configured with valid credentials
|
||||
*/
|
||||
@@ -29,7 +51,7 @@ export const ensureConfigured = async (sessionId) => {
|
||||
/**
|
||||
* Rebuild API client for a session
|
||||
*/
|
||||
export const rebuildApiClient = async (sessionId) => {
|
||||
export const rebuildApiClient = async (sessionId, { refreshCredentials = refreshSessionCredentials } = {}) => {
|
||||
const creds = await getSessionCredentials(sessionId);
|
||||
if (!creds.token || !creds.web_url || !creds.api_web_url) {
|
||||
return null;
|
||||
@@ -44,14 +66,29 @@ export const rebuildApiClient = async (sessionId) => {
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor: always send latest token
|
||||
client.interceptors.request.use((config) => {
|
||||
if (creds.token) {
|
||||
config.headers["X-Acai-Token"] = creds.token;
|
||||
// Always resolve the latest session token. The client outlives individual
|
||||
// JWTs, so capturing `creds` here would keep sending a rotated token.
|
||||
client.interceptors.request.use(async (config) => {
|
||||
const latest = await getSessionCredentials(sessionId);
|
||||
if (latest.token) {
|
||||
config.headers["X-Acai-Token"] = latest.token;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
client.interceptors.response.use(undefined, async (error) => {
|
||||
const original = error.config;
|
||||
if (!isAcaiTokenFailure(error) || !original || original._acaiTokenRetry) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
original._acaiTokenRetry = true;
|
||||
const fresh = await refreshCredentials(sessionId, { force: true });
|
||||
original.headers = original.headers || {};
|
||||
original.headers["X-Acai-Token"] = fresh.token;
|
||||
return client.request(original);
|
||||
});
|
||||
|
||||
sessionApiClients.set(sessionId, client);
|
||||
return client;
|
||||
};
|
||||
@@ -133,10 +170,19 @@ export const withAuth = (handler) => {
|
||||
}, sessionId);
|
||||
}
|
||||
|
||||
await ensureFreshSessionCredentials(sessionId);
|
||||
console.error(`[withAuth] Getting API client for session ${sessionId}...`);
|
||||
await getApiClient(sessionId);
|
||||
console.error(`[withAuth] API client ready, calling handler...`);
|
||||
|
||||
return handler(args, { ...extra, sessionId, inlineCredentials: hasInlineCredentials ? inlineCredentials : null });
|
||||
const handlerExtra = {
|
||||
...extra,
|
||||
sessionId,
|
||||
inlineCredentials: hasInlineCredentials ? inlineCredentials : null,
|
||||
};
|
||||
return runWithTokenRefreshRetry(
|
||||
sessionId,
|
||||
() => handler(args, handlerExtra),
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user