From a91731a83119c81f5677018c52663374ffc5949f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 7 Mar 2026 20:58:09 +0000 Subject: [PATCH] refactor: centralize gateway auth env credential readers --- src/cli/daemon-cli/status.gather.ts | 31 +++++++---------------- src/cli/qr-cli.ts | 27 +------------------- src/commands/dashboard.ts | 10 +------- src/commands/doctor-gateway-auth-token.ts | 7 +---- src/commands/gateway-install-token.ts | 4 +-- src/commands/gateway-status/helpers.ts | 11 +------- src/gateway/call.ts | 25 +++++------------- src/gateway/credentials.ts | 26 ++++++++++++++----- src/gateway/startup-auth.ts | 16 +++++------- 9 files changed, 47 insertions(+), 110 deletions(-) diff --git a/src/cli/daemon-cli/status.gather.ts b/src/cli/daemon-cli/status.gather.ts index 8cefcd95269..71201c6b41e 100644 --- a/src/cli/daemon-cli/status.gather.ts +++ b/src/cli/daemon-cli/status.gather.ts @@ -21,6 +21,11 @@ import type { ServiceConfigAudit } from "../../daemon/service-audit.js"; import { auditGatewayServiceConfig } from "../../daemon/service-audit.js"; import type { GatewayServiceRuntime } from "../../daemon/service-runtime.js"; import { resolveGatewayService } from "../../daemon/service.js"; +import { + readGatewayPasswordEnv, + readGatewayTokenEnv, + trimToUndefined, +} from "../../gateway/credentials.js"; import { resolveGatewayBindHost } from "../../gateway/net.js"; import { formatPortDiagnostics, @@ -106,24 +111,6 @@ function shouldReportPortUsage(status: PortUsageStatus | undefined, rpcOk?: bool return true; } -function trimToUndefined(value: unknown): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); - return trimmed.length > 0 ? trimmed : undefined; -} - -function readGatewayTokenEnv(env: Record): string | undefined { - return trimToUndefined(env.OPENCLAW_GATEWAY_TOKEN) ?? trimToUndefined(env.CLAWDBOT_GATEWAY_TOKEN); -} - -function readGatewayPasswordEnv(env: Record): string | undefined { - return ( - trimToUndefined(env.OPENCLAW_GATEWAY_PASSWORD) ?? trimToUndefined(env.CLAWDBOT_GATEWAY_PASSWORD) - ); -} - async function resolveDaemonProbeToken(params: { daemonCfg: OpenClawConfig; mergedDaemonEnv: Record; @@ -134,7 +121,7 @@ async function resolveDaemonProbeToken(params: { if (explicitToken) { return explicitToken; } - const envToken = readGatewayTokenEnv(params.mergedDaemonEnv); + const envToken = readGatewayTokenEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv); if (envToken) { return envToken; } @@ -154,7 +141,7 @@ async function resolveDaemonProbeToken(params: { if (authMode !== "token") { const passwordCandidate = trimToUndefined(params.explicitPassword) || - readGatewayPasswordEnv(params.mergedDaemonEnv) || + readGatewayPasswordEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv) || (hasConfiguredSecretInput(params.daemonCfg.gateway?.auth?.password, defaults) ? "__configured__" : undefined); @@ -183,7 +170,7 @@ async function resolveDaemonProbePassword(params: { if (explicitPassword) { return explicitPassword; } - const envPassword = readGatewayPasswordEnv(params.mergedDaemonEnv); + const envPassword = readGatewayPasswordEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv); if (envPassword) { return envPassword; } @@ -203,7 +190,7 @@ async function resolveDaemonProbePassword(params: { if (authMode !== "password") { const tokenCandidate = trimToUndefined(params.explicitToken) || - readGatewayTokenEnv(params.mergedDaemonEnv) || + readGatewayTokenEnv(params.mergedDaemonEnv as NodeJS.ProcessEnv) || (hasConfiguredSecretInput(params.daemonCfg.gateway?.auth?.token, defaults) ? "__configured__" : undefined); diff --git a/src/cli/qr-cli.ts b/src/cli/qr-cli.ts index a08d2a10255..45115a50646 100644 --- a/src/cli/qr-cli.ts +++ b/src/cli/qr-cli.ts @@ -2,6 +2,7 @@ import type { Command } from "commander"; import qrcode from "qrcode-terminal"; import { loadConfig } from "../config/config.js"; import { hasConfiguredSecretInput, resolveSecretInputRef } from "../config/types.secrets.js"; +import { readGatewayPasswordEnv, readGatewayTokenEnv } from "../gateway/credentials.js"; import { resolvePairingSetupFromConfig, encodePairingSetupCode } from "../pairing/setup-code.js"; import { runCommandWithTimeout } from "../process/exec.js"; import { defaultRuntime } from "../runtime.js"; @@ -40,32 +41,6 @@ function readDevicePairPublicUrlFromConfig(cfg: ReturnType): return trimmed.length > 0 ? trimmed : undefined; } -function readGatewayTokenEnv(env: NodeJS.ProcessEnv): string | undefined { - const primary = typeof env.OPENCLAW_GATEWAY_TOKEN === "string" ? env.OPENCLAW_GATEWAY_TOKEN : ""; - if (primary.trim().length > 0) { - return primary.trim(); - } - const legacy = typeof env.CLAWDBOT_GATEWAY_TOKEN === "string" ? env.CLAWDBOT_GATEWAY_TOKEN : ""; - if (legacy.trim().length > 0) { - return legacy.trim(); - } - return undefined; -} - -function readGatewayPasswordEnv(env: NodeJS.ProcessEnv): string | undefined { - const primary = - typeof env.OPENCLAW_GATEWAY_PASSWORD === "string" ? env.OPENCLAW_GATEWAY_PASSWORD : ""; - if (primary.trim().length > 0) { - return primary.trim(); - } - const legacy = - typeof env.CLAWDBOT_GATEWAY_PASSWORD === "string" ? env.CLAWDBOT_GATEWAY_PASSWORD : ""; - if (legacy.trim().length > 0) { - return legacy.trim(); - } - return undefined; -} - function shouldResolveLocalGatewayPasswordSecret( cfg: ReturnType, env: NodeJS.ProcessEnv, diff --git a/src/commands/dashboard.ts b/src/commands/dashboard.ts index 02bf23e5897..a25acf75705 100644 --- a/src/commands/dashboard.ts +++ b/src/commands/dashboard.ts @@ -1,6 +1,7 @@ import { readConfigFileSnapshot, resolveGatewayPort } from "../config/config.js"; import type { OpenClawConfig } from "../config/types.js"; import { resolveSecretInputRef } from "../config/types.secrets.js"; +import { readGatewayTokenEnv } from "../gateway/credentials.js"; import { copyToClipboard } from "../infra/clipboard.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; @@ -17,15 +18,6 @@ type DashboardOptions = { noOpen?: boolean; }; -function readGatewayTokenEnv(env: NodeJS.ProcessEnv): string | undefined { - const primary = env.OPENCLAW_GATEWAY_TOKEN?.trim(); - if (primary) { - return primary; - } - const legacy = env.CLAWDBOT_GATEWAY_TOKEN?.trim(); - return legacy || undefined; -} - async function resolveDashboardToken( cfg: OpenClawConfig, env: NodeJS.ProcessEnv = process.env, diff --git a/src/commands/doctor-gateway-auth-token.ts b/src/commands/doctor-gateway-auth-token.ts index dbb69c84d54..782ad26d151 100644 --- a/src/commands/doctor-gateway-auth-token.ts +++ b/src/commands/doctor-gateway-auth-token.ts @@ -1,15 +1,10 @@ import type { OpenClawConfig } from "../config/config.js"; import { resolveSecretInputRef } from "../config/types.secrets.js"; export { shouldRequireGatewayTokenForInstall } from "../gateway/auth-install-policy.js"; +import { readGatewayTokenEnv } from "../gateway/credentials.js"; import { secretRefKey } from "../secrets/ref-contract.js"; import { resolveSecretRefValues } from "../secrets/resolve.js"; -function readGatewayTokenEnv(env: NodeJS.ProcessEnv): string | undefined { - const value = env.OPENCLAW_GATEWAY_TOKEN ?? env.CLAWDBOT_GATEWAY_TOKEN; - const trimmed = value?.trim(); - return trimmed || undefined; -} - export async function resolveGatewayAuthTokenForService( cfg: OpenClawConfig, env: NodeJS.ProcessEnv, diff --git a/src/commands/gateway-install-token.ts b/src/commands/gateway-install-token.ts index a7293a7bc9e..2f9e86bd867 100644 --- a/src/commands/gateway-install-token.ts +++ b/src/commands/gateway-install-token.ts @@ -4,6 +4,7 @@ import { resolveSecretInputRef } from "../config/types.secrets.js"; import { shouldRequireGatewayTokenForInstall } from "../gateway/auth-install-policy.js"; import { hasAmbiguousGatewayAuthModeConfig } from "../gateway/auth-mode-policy.js"; import { resolveGatewayAuth } from "../gateway/auth.js"; +import { readGatewayTokenEnv } from "../gateway/credentials.js"; import { secretRefKey } from "../secrets/ref-contract.js"; import { resolveSecretRefValues } from "../secrets/resolve.js"; import { randomToken } from "./onboard-helpers.js"; @@ -45,8 +46,7 @@ export async function resolveGatewayInstallToken( ? undefined : cfg.gateway.auth.token.trim() || undefined; const explicitToken = options.explicitToken?.trim() || undefined; - const envToken = - options.env.OPENCLAW_GATEWAY_TOKEN?.trim() || options.env.CLAWDBOT_GATEWAY_TOKEN?.trim(); + const envToken = readGatewayTokenEnv(options.env); if (hasAmbiguousGatewayAuthModeConfig(cfg)) { return { diff --git a/src/commands/gateway-status/helpers.ts b/src/commands/gateway-status/helpers.ts index 2386870beba..6ee1ccd6b35 100644 --- a/src/commands/gateway-status/helpers.ts +++ b/src/commands/gateway-status/helpers.ts @@ -1,6 +1,7 @@ import { resolveGatewayPort } from "../../config/config.js"; import type { OpenClawConfig, ConfigFileSnapshot } from "../../config/types.js"; import { hasConfiguredSecretInput } from "../../config/types.secrets.js"; +import { readGatewayPasswordEnv, readGatewayTokenEnv } from "../../gateway/credentials.js"; import type { GatewayProbeResult } from "../../gateway/probe.js"; import { resolveConfiguredSecretInputString } from "../../gateway/resolve-configured-secret-input-string.js"; import { pickPrimaryTailnetIPv4 } from "../../infra/tailnet.js"; @@ -146,16 +147,6 @@ export function sanitizeSshTarget(value: unknown): string | null { return trimmed.replace(/^ssh\\s+/, ""); } -function readGatewayTokenEnv(env: NodeJS.ProcessEnv = process.env): string | undefined { - const token = env.OPENCLAW_GATEWAY_TOKEN?.trim() || env.CLAWDBOT_GATEWAY_TOKEN?.trim(); - return token || undefined; -} - -function readGatewayPasswordEnv(env: NodeJS.ProcessEnv = process.env): string | undefined { - const password = env.OPENCLAW_GATEWAY_PASSWORD?.trim() || env.CLAWDBOT_GATEWAY_PASSWORD?.trim(); - return password || undefined; -} - export async function resolveAuthForTarget( cfg: OpenClawConfig, target: GatewayStatusTarget, diff --git a/src/gateway/call.ts b/src/gateway/call.ts index 5d036a0d32a..07d5100cf7e 100644 --- a/src/gateway/call.ts +++ b/src/gateway/call.ts @@ -18,7 +18,12 @@ import { } from "../utils/message-channel.js"; import { VERSION } from "../version.js"; import { GatewayClient } from "./client.js"; -import { resolveGatewayCredentialsFromConfig } from "./credentials.js"; +import { + readGatewayPasswordEnv, + readGatewayTokenEnv, + resolveGatewayCredentialsFromConfig, + trimToUndefined, +} from "./credentials.js"; import { CLI_DEFAULT_OPERATOR_SCOPES, resolveLeastPrivilegeOperatorScopesForMethod, @@ -235,24 +240,6 @@ type ResolvedGatewayCallContext = { explicitAuth: ExplicitGatewayAuth; }; -function trimToUndefined(value: unknown): string | undefined { - if (typeof value !== "string") { - return undefined; - } - const trimmed = value.trim(); - return trimmed.length > 0 ? trimmed : undefined; -} - -function readGatewayTokenEnv(env: NodeJS.ProcessEnv): string | undefined { - return trimToUndefined(env.OPENCLAW_GATEWAY_TOKEN) ?? trimToUndefined(env.CLAWDBOT_GATEWAY_TOKEN); -} - -function readGatewayPasswordEnv(env: NodeJS.ProcessEnv): string | undefined { - return ( - trimToUndefined(env.OPENCLAW_GATEWAY_PASSWORD) ?? trimToUndefined(env.CLAWDBOT_GATEWAY_PASSWORD) - ); -} - function resolveGatewayCallTimeout(timeoutValue: unknown): { timeoutMs: number; safeTimerTimeoutMs: number; diff --git a/src/gateway/credentials.ts b/src/gateway/credentials.ts index 88c8a86088b..42cc36676b9 100644 --- a/src/gateway/credentials.ts +++ b/src/gateway/credentials.ts @@ -69,9 +69,9 @@ function throwUnresolvedGatewaySecretInput(path: string): never { throw new GatewaySecretRefUnavailableError(path); } -function readGatewayTokenEnv( - env: NodeJS.ProcessEnv, - includeLegacyEnv: boolean, +export function readGatewayTokenEnv( + env: NodeJS.ProcessEnv = process.env, + includeLegacyEnv = true, ): string | undefined { const primary = trimToUndefined(env.OPENCLAW_GATEWAY_TOKEN); if (primary) { @@ -83,9 +83,9 @@ function readGatewayTokenEnv( return trimToUndefined(env.CLAWDBOT_GATEWAY_TOKEN); } -function readGatewayPasswordEnv( - env: NodeJS.ProcessEnv, - includeLegacyEnv: boolean, +export function readGatewayPasswordEnv( + env: NodeJS.ProcessEnv = process.env, + includeLegacyEnv = true, ): string | undefined { const primary = trimToUndefined(env.OPENCLAW_GATEWAY_PASSWORD); if (primary) { @@ -97,6 +97,20 @@ function readGatewayPasswordEnv( return trimToUndefined(env.CLAWDBOT_GATEWAY_PASSWORD); } +export function hasGatewayTokenEnvCandidate( + env: NodeJS.ProcessEnv = process.env, + includeLegacyEnv = true, +): boolean { + return Boolean(readGatewayTokenEnv(env, includeLegacyEnv)); +} + +export function hasGatewayPasswordEnvCandidate( + env: NodeJS.ProcessEnv = process.env, + includeLegacyEnv = true, +): boolean { + return Boolean(readGatewayPasswordEnv(env, includeLegacyEnv)); +} + export function resolveGatewayCredentialsFromValues(params: { configToken?: unknown; configPassword?: unknown; diff --git a/src/gateway/startup-auth.ts b/src/gateway/startup-auth.ts index 74cf0480eb1..8132d902b78 100644 --- a/src/gateway/startup-auth.ts +++ b/src/gateway/startup-auth.ts @@ -10,6 +10,11 @@ import { secretRefKey } from "../secrets/ref-contract.js"; import { resolveSecretRefValues } from "../secrets/resolve.js"; import { assertExplicitGatewayAuthModeWhenBothConfigured } from "./auth-mode-policy.js"; import { resolveGatewayAuth, type ResolvedGatewayAuth } from "./auth.js"; +import { + hasGatewayPasswordEnvCandidate, + hasGatewayTokenEnvCandidate, + readGatewayTokenEnv, +} from "./credentials.js"; export function mergeGatewayAuthConfig( base?: GatewayAuthConfig, @@ -97,8 +102,7 @@ function hasGatewayTokenCandidate(params: { env: NodeJS.ProcessEnv; authOverride?: GatewayAuthConfig; }): boolean { - const envToken = - params.env.OPENCLAW_GATEWAY_TOKEN?.trim() || params.env.CLAWDBOT_GATEWAY_TOKEN?.trim(); + const envToken = readGatewayTokenEnv(params.env); if (envToken) { return true; } @@ -117,14 +121,6 @@ function hasGatewayTokenOverrideCandidate(params: { authOverride?: GatewayAuthCo ); } -function hasGatewayTokenEnvCandidate(env: NodeJS.ProcessEnv): boolean { - return Boolean(env.OPENCLAW_GATEWAY_TOKEN?.trim() || env.CLAWDBOT_GATEWAY_TOKEN?.trim()); -} - -function hasGatewayPasswordEnvCandidate(env: NodeJS.ProcessEnv): boolean { - return Boolean(env.OPENCLAW_GATEWAY_PASSWORD?.trim() || env.CLAWDBOT_GATEWAY_PASSWORD?.trim()); -} - function hasGatewayPasswordOverrideCandidate(params: { env: NodeJS.ProcessEnv; authOverride?: GatewayAuthConfig;