diff --git a/src/gateway/connection-auth.test.ts b/src/gateway/connection-auth.test.ts index 036b9ff82dc..32ed16b045d 100644 --- a/src/gateway/connection-auth.test.ts +++ b/src/gateway/connection-auth.test.ts @@ -20,6 +20,64 @@ function cfg(input: Partial): OpenClawConfig { return input as OpenClawConfig; } +function createRemoteModeConfig() { + return { + gateway: { + mode: "remote" as const, + auth: { + token: "local-token", + password: "local-password", // pragma: allowlist secret + }, + remote: { + url: "wss://remote.example", + token: "remote-token", + password: "remote-password", // pragma: allowlist secret + }, + }, + }; +} + +function createUnresolvedLocalAuthConfig(params: { + mode: "token" | "password"; + id: string; + remoteFallback: string; +}) { + return cfg({ + gateway: { + mode: "local", + auth: { + mode: params.mode, + [params.mode]: { source: "env", provider: "default", id: params.id }, + }, + remote: { + [params.mode]: params.remoteFallback, + }, + }, + secrets: { + providers: { + default: { source: "env" }, + }, + }, + }); +} + +async function expectFailClosedOnUnresolvedLocalAuth(config: OpenClawConfig, path: string) { + await expect( + resolveGatewayConnectionAuth({ + config, + env: {} as NodeJS.ProcessEnv, + includeLegacyEnv: false, + }), + ).rejects.toThrow(path); + expect(() => + resolveGatewayConnectionAuthFromConfig({ + cfg: config, + env: {} as NodeJS.ProcessEnv, + includeLegacyEnv: false, + }), + ).toThrow(path); +} + const DEFAULT_ENV = { OPENCLAW_GATEWAY_TOKEN: "env-token", OPENCLAW_GATEWAY_PASSWORD: "env-password", // pragma: allowlist secret @@ -93,20 +151,7 @@ describe("resolveGatewayConnectionAuth", () => { }, { name: "remote mode defaults to remote-first token and env-first password", - cfg: cfg({ - gateway: { - mode: "remote", - auth: { - token: "local-token", - password: "local-password", // pragma: allowlist secret - }, - remote: { - url: "wss://remote.example", - token: "remote-token", - password: "remote-password", // pragma: allowlist secret - }, - }, - }), + cfg: cfg(createRemoteModeConfig()), env: DEFAULT_ENV, expected: { token: "remote-token", @@ -115,20 +160,7 @@ describe("resolveGatewayConnectionAuth", () => { }, { name: "remote mode supports env-first token with remote-first password", - cfg: cfg({ - gateway: { - mode: "remote", - auth: { - token: "local-token", - password: "local-password", // pragma: allowlist secret - }, - remote: { - url: "wss://remote.example", - token: "remote-token", - password: "remote-password", // pragma: allowlist secret - }, - }, - }), + cfg: cfg(createRemoteModeConfig()), env: DEFAULT_ENV, options: { remoteTokenPrecedence: "env-first", @@ -418,72 +450,24 @@ describe("resolveGatewayConnectionAuth", () => { }); it("fails closed when local token SecretRef is unresolved and remote token fallback exists", async () => { - const config = cfg({ - gateway: { - mode: "local", - auth: { - mode: "token", - token: { source: "env", provider: "default", id: "MISSING_LOCAL_TOKEN" }, - }, - remote: { - token: "remote-token", - }, - }, - secrets: { - providers: { - default: { source: "env" }, - }, - }, - }); - - await expect( - resolveGatewayConnectionAuth({ - config, - env: {} as NodeJS.ProcessEnv, - includeLegacyEnv: false, + await expectFailClosedOnUnresolvedLocalAuth( + createUnresolvedLocalAuthConfig({ + mode: "token", + id: "MISSING_LOCAL_TOKEN", + remoteFallback: "remote-token", }), - ).rejects.toThrow("gateway.auth.token"); - expect(() => - resolveGatewayConnectionAuthFromConfig({ - cfg: config, - env: {} as NodeJS.ProcessEnv, - includeLegacyEnv: false, - }), - ).toThrow("gateway.auth.token"); + "gateway.auth.token", + ); }); it("fails closed when local password SecretRef is unresolved and remote password fallback exists", async () => { - const config = cfg({ - gateway: { - mode: "local", - auth: { - mode: "password", - password: { source: "env", provider: "default", id: "MISSING_LOCAL_PASSWORD" }, - }, - remote: { - password: "remote-password", // pragma: allowlist secret - }, - }, - secrets: { - providers: { - default: { source: "env" }, - }, - }, - }); - - await expect( - resolveGatewayConnectionAuth({ - config, - env: {} as NodeJS.ProcessEnv, - includeLegacyEnv: false, + await expectFailClosedOnUnresolvedLocalAuth( + createUnresolvedLocalAuthConfig({ + mode: "password", + id: "MISSING_LOCAL_PASSWORD", + remoteFallback: "remote-password", // pragma: allowlist secret }), - ).rejects.toThrow("gateway.auth.password"); - expect(() => - resolveGatewayConnectionAuthFromConfig({ - cfg: config, - env: {} as NodeJS.ProcessEnv, - includeLegacyEnv: false, - }), - ).toThrow("gateway.auth.password"); + "gateway.auth.password", + ); }); }); diff --git a/src/gateway/connection-auth.ts b/src/gateway/connection-auth.ts index 11c40395af6..4f8f957528f 100644 --- a/src/gateway/connection-auth.ts +++ b/src/gateway/connection-auth.ts @@ -25,30 +25,10 @@ export type GatewayConnectionAuthOptions = { remotePasswordFallback?: GatewayRemoteCredentialFallback; }; -export async function resolveGatewayConnectionAuth( - params: GatewayConnectionAuthOptions, -): Promise<{ token?: string; password?: string }> { - return await resolveGatewayCredentialsWithSecretInputs({ - config: params.config, - env: params.env, - explicitAuth: params.explicitAuth, - urlOverride: params.urlOverride, - urlOverrideSource: params.urlOverrideSource, - modeOverride: params.modeOverride, - includeLegacyEnv: params.includeLegacyEnv, - localTokenPrecedence: params.localTokenPrecedence, - localPasswordPrecedence: params.localPasswordPrecedence, - remoteTokenPrecedence: params.remoteTokenPrecedence, - remotePasswordPrecedence: params.remotePasswordPrecedence, - remoteTokenFallback: params.remoteTokenFallback, - remotePasswordFallback: params.remotePasswordFallback, - }); -} - -export function resolveGatewayConnectionAuthFromConfig( +function toGatewayCredentialOptions( params: Omit & { cfg: OpenClawConfig }, -): { token?: string; password?: string } { - return resolveGatewayCredentialsFromConfig({ +) { + return { cfg: params.cfg, env: params.env, explicitAuth: params.explicitAuth, @@ -62,5 +42,20 @@ export function resolveGatewayConnectionAuthFromConfig( remotePasswordPrecedence: params.remotePasswordPrecedence, remoteTokenFallback: params.remoteTokenFallback, remotePasswordFallback: params.remotePasswordFallback, + }; +} + +export async function resolveGatewayConnectionAuth( + params: GatewayConnectionAuthOptions, +): Promise<{ token?: string; password?: string }> { + return await resolveGatewayCredentialsWithSecretInputs({ + config: params.config, + ...toGatewayCredentialOptions({ ...params, cfg: params.config }), }); } + +export function resolveGatewayConnectionAuthFromConfig( + params: Omit & { cfg: OpenClawConfig }, +): { token?: string; password?: string } { + return resolveGatewayCredentialsFromConfig(toGatewayCredentialOptions(params)); +}