Gateway: add SecretRef support for gateway.auth.token with auth-mode guardrails (#35094)

This commit is contained in:
Josh Avant
2026-03-05 12:53:56 -06:00
committed by GitHub
parent bc66a8fa81
commit 72cf9253fc
112 changed files with 5750 additions and 465 deletions

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../config/config.js";
import { resolveGatewayPort } from "../config/config.js";
import { isValidEnvSecretRefId, type SecretInput } from "../config/types.secrets.js";
import {
maybeAddTailnetOriginToControlUiAllowedOrigins,
TAILSCALE_DOCS_LINES,
@@ -8,6 +9,7 @@ import {
} from "../gateway/gateway-config-prompts.shared.js";
import { findTailscaleBinary } from "../infra/tailscale.js";
import type { RuntimeEnv } from "../runtime.js";
import { resolveDefaultSecretProviderAlias } from "../secrets/ref-contract.js";
import { validateIPv4AddressInput } from "../shared/net/ipv4.js";
import { note } from "../terminal/note.js";
import { buildGatewayAuthConfig } from "./configure.gateway-auth.js";
@@ -20,6 +22,7 @@ import {
} from "./onboard-helpers.js";
type GatewayAuthChoice = "token" | "password" | "trusted-proxy";
type GatewayTokenInputMode = "plaintext" | "ref";
export async function promptGatewayConfig(
cfg: OpenClawConfig,
@@ -156,7 +159,8 @@ export async function promptGatewayConfig(
tailscaleResetOnExit = false;
}
let gatewayToken: string | undefined;
let gatewayToken: SecretInput | undefined;
let gatewayTokenForCalls: string | undefined;
let gatewayPassword: string | undefined;
let trustedProxyConfig:
| { userHeader: string; requiredHeaders?: string[]; allowUsers?: string[] }
@@ -165,14 +169,65 @@ export async function promptGatewayConfig(
let next = cfg;
if (authMode === "token") {
const tokenInput = guardCancel(
await text({
message: "Gateway token (blank to generate)",
initialValue: randomToken(),
const tokenInputMode = guardCancel(
await select<GatewayTokenInputMode>({
message: "Gateway token source",
options: [
{
value: "plaintext",
label: "Generate/store plaintext token",
hint: "Default",
},
{
value: "ref",
label: "Use SecretRef",
hint: "Store an env-backed reference instead of plaintext",
},
],
initialValue: "plaintext",
}),
runtime,
);
gatewayToken = normalizeGatewayTokenInput(tokenInput) || randomToken();
if (tokenInputMode === "ref") {
const envVar = guardCancel(
await text({
message: "Gateway token env var",
initialValue: "OPENCLAW_GATEWAY_TOKEN",
placeholder: "OPENCLAW_GATEWAY_TOKEN",
validate: (value) => {
const candidate = String(value ?? "").trim();
if (!isValidEnvSecretRefId(candidate)) {
return "Use an env var name like OPENCLAW_GATEWAY_TOKEN.";
}
const resolved = process.env[candidate]?.trim();
if (!resolved) {
return `Environment variable "${candidate}" is missing or empty in this session.`;
}
return undefined;
},
}),
runtime,
);
const envVarName = String(envVar ?? "").trim();
gatewayToken = {
source: "env",
provider: resolveDefaultSecretProviderAlias(cfg, "env", {
preferFirstProviderForSource: true,
}),
id: envVarName,
};
note(`Validated ${envVarName}. OpenClaw will store a token SecretRef.`, "Gateway token");
} else {
const tokenInput = guardCancel(
await text({
message: "Gateway token (blank to generate)",
initialValue: randomToken(),
}),
runtime,
);
gatewayTokenForCalls = normalizeGatewayTokenInput(tokenInput) || randomToken();
gatewayToken = gatewayTokenForCalls;
}
}
if (authMode === "password") {
@@ -294,5 +349,5 @@ export async function promptGatewayConfig(
tailscaleBin,
});
return { config: next, port, token: gatewayToken };
return { config: next, port, token: gatewayTokenForCalls };
}