feat(openclaw): support x-openclaw-token header alongside legacy x-openclaw-auth

Accept x-openclaw-token as the preferred auth header for OpenClaw
invite/join flows, falling back to x-openclaw-auth for backwards
compatibility. Update diagnostics messages accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-06 16:50:20 -06:00
parent 514dc43923
commit e4928f3a10
2 changed files with 64 additions and 9 deletions

View File

@@ -90,6 +90,41 @@ describe("buildJoinDefaultsPayloadForAccept", () => {
}); });
}); });
it("accepts auth from agentDefaultsPayload.headers.x-openclaw-token", () => {
const result = buildJoinDefaultsPayloadForAccept({
adapterType: "openclaw",
defaultsPayload: {
url: "http://127.0.0.1:18789/hooks/agent",
method: "POST",
headers: {
"x-openclaw-token": "gateway-token",
},
},
}) as Record<string, unknown>;
expect(result).toMatchObject({
headers: {
"x-openclaw-token": "gateway-token",
},
webhookAuthHeader: "Bearer gateway-token",
});
});
it("accepts inbound x-openclaw-token compatibility header", () => {
const result = buildJoinDefaultsPayloadForAccept({
adapterType: "openclaw",
defaultsPayload: null,
inboundOpenClawTokenHeader: "gateway-token",
}) as Record<string, unknown>;
expect(result).toMatchObject({
headers: {
"x-openclaw-token": "gateway-token",
},
webhookAuthHeader: "Bearer gateway-token",
});
});
it("accepts wrapped auth values in headers for compatibility", () => { it("accepts wrapped auth values in headers for compatibility", () => {
const result = buildJoinDefaultsPayloadForAccept({ const result = buildJoinDefaultsPayloadForAccept({
adapterType: "openclaw", adapterType: "openclaw",

View File

@@ -320,6 +320,7 @@ export function buildJoinDefaultsPayloadForAccept(input: {
paperclipApiUrl?: unknown; paperclipApiUrl?: unknown;
webhookAuthHeader?: unknown; webhookAuthHeader?: unknown;
inboundOpenClawAuthHeader?: string | null; inboundOpenClawAuthHeader?: string | null;
inboundOpenClawTokenHeader?: string | null;
}): unknown { }): unknown {
if (input.adapterType !== "openclaw") { if (input.adapterType !== "openclaw") {
return input.defaultsPayload; return input.defaultsPayload;
@@ -367,6 +368,15 @@ export function buildJoinDefaultsPayloadForAccept(input: {
const inboundOpenClawAuthHeader = nonEmptyTrimmedString( const inboundOpenClawAuthHeader = nonEmptyTrimmedString(
input.inboundOpenClawAuthHeader input.inboundOpenClawAuthHeader
); );
const inboundOpenClawTokenHeader = nonEmptyTrimmedString(
input.inboundOpenClawTokenHeader
);
if (
inboundOpenClawTokenHeader &&
!headerMapHasKeyIgnoreCase(mergedHeaders, "x-openclaw-token")
) {
mergedHeaders["x-openclaw-token"] = inboundOpenClawTokenHeader;
}
if ( if (
inboundOpenClawAuthHeader && inboundOpenClawAuthHeader &&
!headerMapHasKeyIgnoreCase(mergedHeaders, "x-openclaw-auth") !headerMapHasKeyIgnoreCase(mergedHeaders, "x-openclaw-auth")
@@ -388,7 +398,9 @@ export function buildJoinDefaultsPayloadForAccept(input: {
nonEmptyTrimmedString(merged.webhookAuthHeader) nonEmptyTrimmedString(merged.webhookAuthHeader)
); );
if (!hasAuthorizationHeader && !hasWebhookAuthHeader) { if (!hasAuthorizationHeader && !hasWebhookAuthHeader) {
const openClawAuthToken = headerMapGetIgnoreCase( const openClawAuthToken =
headerMapGetIgnoreCase(mergedHeaders, "x-openclaw-token") ??
headerMapGetIgnoreCase(
mergedHeaders, mergedHeaders,
"x-openclaw-auth" "x-openclaw-auth"
); );
@@ -484,9 +496,8 @@ function summarizeOpenClawDefaultsForLog(defaultsPayload: unknown) {
: null; : null;
const headers = defaults ? normalizeHeaderMap(defaults.headers) : undefined; const headers = defaults ? normalizeHeaderMap(defaults.headers) : undefined;
const openClawAuthHeaderValue = headers const openClawAuthHeaderValue = headers
? Object.entries(headers).find( ? headerMapGetIgnoreCase(headers, "x-openclaw-token") ??
([key]) => key.trim().toLowerCase() === "x-openclaw-auth" headerMapGetIgnoreCase(headers, "x-openclaw-auth")
)?.[1] ?? null
: null; : null;
return { return {
@@ -703,20 +714,23 @@ function normalizeAgentDefaultsForJoin(input: {
} }
const openClawAuthHeader = headers const openClawAuthHeader = headers
? headerMapGetIgnoreCase(headers, "x-openclaw-auth") ? headerMapGetIgnoreCase(headers, "x-openclaw-token") ??
headerMapGetIgnoreCase(headers, "x-openclaw-auth")
: null; : null;
if (openClawAuthHeader) { if (openClawAuthHeader) {
diagnostics.push({ diagnostics.push({
code: "openclaw_auth_header_configured", code: "openclaw_auth_header_configured",
level: "info", level: "info",
message: "Gateway auth token received via headers.x-openclaw-auth." message:
"Gateway auth token received via headers.x-openclaw-token (or legacy x-openclaw-auth)."
}); });
} else { } else {
diagnostics.push({ diagnostics.push({
code: "openclaw_auth_header_missing", code: "openclaw_auth_header_missing",
level: "warn", level: "warn",
message: "Gateway auth token is missing from agent defaults.", message: "Gateway auth token is missing from agent defaults.",
hint: "Set agentDefaultsPayload.headers.x-openclaw-auth to the token your OpenClaw endpoint requires." hint:
"Set agentDefaultsPayload.headers.x-openclaw-token (or legacy x-openclaw-auth) to the token your OpenClaw endpoint requires."
}); });
} }
@@ -1894,7 +1908,8 @@ export function accessRoutes(
responsesWebhookHeaders: req.body.responsesWebhookHeaders ?? null, responsesWebhookHeaders: req.body.responsesWebhookHeaders ?? null,
paperclipApiUrl: req.body.paperclipApiUrl ?? null, paperclipApiUrl: req.body.paperclipApiUrl ?? null,
webhookAuthHeader: req.body.webhookAuthHeader ?? null, webhookAuthHeader: req.body.webhookAuthHeader ?? null,
inboundOpenClawAuthHeader: req.header("x-openclaw-auth") ?? null inboundOpenClawAuthHeader: req.header("x-openclaw-auth") ?? null,
inboundOpenClawTokenHeader: req.header("x-openclaw-token") ?? null
}) })
: null; : null;
@@ -1917,6 +1932,9 @@ export function accessRoutes(
inboundOpenClawAuthHeader: summarizeSecretForLog( inboundOpenClawAuthHeader: summarizeSecretForLog(
req.header("x-openclaw-auth") ?? null req.header("x-openclaw-auth") ?? null
), ),
inboundOpenClawTokenHeader: summarizeSecretForLog(
req.header("x-openclaw-token") ?? null
),
rawAgentDefaults: summarizeOpenClawDefaultsForLog( rawAgentDefaults: summarizeOpenClawDefaultsForLog(
req.body.agentDefaultsPayload ?? null req.body.agentDefaultsPayload ?? null
), ),
@@ -2107,7 +2125,9 @@ export function accessRoutes(
expectedDefaults.openClawAuthHeader && expectedDefaults.openClawAuthHeader &&
!persistedDefaults.openClawAuthHeader !persistedDefaults.openClawAuthHeader
) { ) {
missingPersistedFields.push("headers.x-openclaw-auth"); missingPersistedFields.push(
"headers.x-openclaw-token|headers.x-openclaw-auth"
);
} }
if ( if (
expectedDefaults.headerKeys.length > 0 && expectedDefaults.headerKeys.length > 0 &&