2026-03-05 00:04:26 -06:00
|
|
|
import { logger } from "../logger";
|
2026-03-21 17:03:42 -05:00
|
|
|
import { timingSafeEqual } from "crypto";
|
|
|
|
|
|
2026-06-08 22:33:34 -05:00
|
|
|
let warnedDisableAuthIgnored = false;
|
|
|
|
|
let loggedAuthDisabledByEnv = false;
|
|
|
|
|
let loggedAuthDisabledNoToken = false;
|
2026-08-24 09:35:03 -05:00
|
|
|
let warnedLegacyQueryAuth = false;
|
2026-06-08 22:33:34 -05:00
|
|
|
|
2026-03-21 17:03:42 -05:00
|
|
|
function safeCompare(a: string, b: string): boolean {
|
|
|
|
|
const bufA = Buffer.from(a);
|
|
|
|
|
const bufB = Buffer.from(b);
|
|
|
|
|
if (bufA.length !== bufB.length) return false;
|
|
|
|
|
return timingSafeEqual(bufA, bufB);
|
|
|
|
|
}
|
2026-03-05 00:04:26 -06:00
|
|
|
|
|
|
|
|
export function isAuthorizedAgentRequest(
|
|
|
|
|
req: Request,
|
|
|
|
|
url: URL,
|
|
|
|
|
agentToken?: string,
|
|
|
|
|
): boolean {
|
|
|
|
|
const disableAuth =
|
|
|
|
|
String(process.env.OVERLORD_DISABLE_AGENT_AUTH || "").toLowerCase() ===
|
|
|
|
|
"true";
|
|
|
|
|
if (disableAuth) {
|
2026-04-08 21:24:23 -05:00
|
|
|
const nodeEnv = String(process.env.NODE_ENV || "development").toLowerCase();
|
|
|
|
|
if (nodeEnv === "production") {
|
2026-06-08 22:33:34 -05:00
|
|
|
if (!warnedDisableAuthIgnored) {
|
|
|
|
|
warnedDisableAuthIgnored = true;
|
|
|
|
|
logger.warn("[auth] OVERLORD_DISABLE_AGENT_AUTH is ignored in production mode");
|
|
|
|
|
}
|
2026-04-08 21:24:23 -05:00
|
|
|
} else {
|
2026-06-08 22:33:34 -05:00
|
|
|
if (!loggedAuthDisabledByEnv) {
|
|
|
|
|
loggedAuthDisabledByEnv = true;
|
|
|
|
|
logger.info("[auth] Agent auth explicitly disabled by OVERLORD_DISABLE_AGENT_AUTH=true (non-production mode)");
|
|
|
|
|
}
|
2026-04-08 21:24:23 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
2026-03-05 00:04:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const token = agentToken?.trim();
|
|
|
|
|
if (!token) {
|
2026-06-08 22:33:34 -05:00
|
|
|
if (!loggedAuthDisabledNoToken) {
|
|
|
|
|
loggedAuthDisabledNoToken = true;
|
2026-08-24 09:35:03 -05:00
|
|
|
logger.error("[auth] Agent auth token is missing; agent requests will be rejected");
|
2026-06-08 22:33:34 -05:00
|
|
|
}
|
2026-08-24 09:35:03 -05:00
|
|
|
return false;
|
2026-03-05 00:04:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const headerToken = req.headers.get("x-agent-token");
|
2026-08-24 09:35:03 -05:00
|
|
|
if (headerToken !== null && safeCompare(headerToken, token)) return true;
|
|
|
|
|
|
|
|
|
|
const legacyQuerySetting = String(process.env.OVERLORD_ALLOW_AGENT_TOKEN_QUERY || "").trim().toLowerCase();
|
|
|
|
|
const allowLegacyQuery = legacyQuerySetting !== "false" && legacyQuerySetting !== "0";
|
|
|
|
|
if (!allowLegacyQuery) return false;
|
|
|
|
|
if (!warnedLegacyQueryAuth) {
|
|
|
|
|
warnedLegacyQueryAuth = true;
|
|
|
|
|
logger.warn("[auth] Legacy agent query-token authentication is enabled; URLs may leak credentials");
|
|
|
|
|
}
|
2026-03-05 00:04:26 -06:00
|
|
|
const queryToken = url.searchParams.get("token");
|
2026-08-24 09:35:03 -05:00
|
|
|
const isAuthed = queryToken !== null && safeCompare(queryToken, token);
|
2026-03-05 00:04:26 -06:00
|
|
|
|
|
|
|
|
return isAuthed;
|
|
|
|
|
}
|