Compare commits
2 Commits
main
...
fix/contro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce267400c | ||
|
|
d353db42ba |
@ -29,6 +29,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Model catalog: avoid caching import failures, log transient discovery errors, and keep partial results. (#1332) — thanks @dougvk.
|
- Model catalog: avoid caching import failures, log transient discovery errors, and keep partial results. (#1332) — thanks @dougvk.
|
||||||
- Doctor: clarify plugin auto-enable hint text in the startup banner.
|
- Doctor: clarify plugin auto-enable hint text in the startup banner.
|
||||||
- Gateway: clarify unauthorized handshake responses with token/password mismatch guidance.
|
- Gateway: clarify unauthorized handshake responses with token/password mismatch guidance.
|
||||||
|
- Gateway: allow token-auth Control UI connections without device identity. (#1314) — thanks @dbhurley.
|
||||||
- Gateway: preserve restart wake routing + thread replies across restarts. (#1337) — thanks @John-Rood.
|
- Gateway: preserve restart wake routing + thread replies across restarts. (#1337) — thanks @John-Rood.
|
||||||
- Gateway: reschedule per-agent heartbeats on config hot reload without restarting the runner.
|
- Gateway: reschedule per-agent heartbeats on config hot reload without restarting the runner.
|
||||||
- Config: log invalid config issues once per run and keep invalid-config errors stackless.
|
- Config: log invalid config issues once per run and keep invalid-config errors stackless.
|
||||||
|
|||||||
@ -102,6 +102,33 @@ describe("gateway server auth/connect", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("accepts token auth without device identity", async () => {
|
||||||
|
const { server, ws, prevToken } = await startServerWithClient("secret");
|
||||||
|
const res = await connectReq(ws, { token: "secret", device: null });
|
||||||
|
expect(res.ok).toBe(true);
|
||||||
|
ws.close();
|
||||||
|
await server.close();
|
||||||
|
if (prevToken === undefined) {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
} else {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("requires device identity when auth mode is none", async () => {
|
||||||
|
const { server, ws, prevToken } = await startServerWithClient();
|
||||||
|
const res = await connectReq(ws, { device: null });
|
||||||
|
expect(res.ok).toBe(false);
|
||||||
|
expect(res.error?.message ?? "").toContain("device identity required");
|
||||||
|
ws.close();
|
||||||
|
await server.close();
|
||||||
|
if (prevToken === undefined) {
|
||||||
|
delete process.env.CLAWDBOT_GATEWAY_TOKEN;
|
||||||
|
} else {
|
||||||
|
process.env.CLAWDBOT_GATEWAY_TOKEN = prevToken;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("accepts password auth when configured", async () => {
|
test("accepts password auth when configured", async () => {
|
||||||
testState.gatewayAuth = { mode: "password", password: "secret" };
|
testState.gatewayAuth = { mode: "password", password: "secret" };
|
||||||
const port = await getFreePort();
|
const port = await getFreePort();
|
||||||
|
|||||||
@ -290,9 +290,18 @@ export function attachGatewayWsMessageHandler(params: {
|
|||||||
connectParams.role = role;
|
connectParams.role = role;
|
||||||
connectParams.scopes = scopes;
|
connectParams.scopes = scopes;
|
||||||
|
|
||||||
|
const authResult = await authorizeGatewayConnect({
|
||||||
|
auth: resolvedAuth,
|
||||||
|
connectAuth: connectParams.auth,
|
||||||
|
req: upgradeReq,
|
||||||
|
});
|
||||||
|
let authOk = authResult.ok;
|
||||||
|
let authMethod = authResult.method ?? "none";
|
||||||
|
const allowsDeviceOptional = authOk && authMethod === "token";
|
||||||
|
|
||||||
const device = connectParams.device;
|
const device = connectParams.device;
|
||||||
let devicePublicKey: string | null = null;
|
let devicePublicKey: string | null = null;
|
||||||
if (!device) {
|
if (!device && !allowsDeviceOptional) {
|
||||||
setHandshakeState("failed");
|
setHandshakeState("failed");
|
||||||
setCloseCause("device-required", {
|
setCloseCause("device-required", {
|
||||||
client: connectParams.client.id,
|
client: connectParams.client.id,
|
||||||
@ -458,14 +467,7 @@ export function attachGatewayWsMessageHandler(params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const authResult = await authorizeGatewayConnect({
|
if (!authOk && connectParams.auth?.token && device) {
|
||||||
auth: resolvedAuth,
|
|
||||||
connectAuth: connectParams.auth,
|
|
||||||
req: upgradeReq,
|
|
||||||
});
|
|
||||||
let authOk = authResult.ok;
|
|
||||||
let authMethod = authResult.method ?? "none";
|
|
||||||
if (!authOk && connectParams.auth?.token) {
|
|
||||||
const tokenCheck = await verifyDeviceToken({
|
const tokenCheck = await verifyDeviceToken({
|
||||||
deviceId: device.id,
|
deviceId: device.id,
|
||||||
token: connectParams.auth.token,
|
token: connectParams.auth.token,
|
||||||
|
|||||||
@ -280,7 +280,7 @@ export async function connectReq(
|
|||||||
signature: string;
|
signature: string;
|
||||||
signedAt: number;
|
signedAt: number;
|
||||||
nonce?: string;
|
nonce?: string;
|
||||||
};
|
} | null;
|
||||||
},
|
},
|
||||||
): Promise<ConnectResponse> {
|
): Promise<ConnectResponse> {
|
||||||
const { randomUUID } = await import("node:crypto");
|
const { randomUUID } = await import("node:crypto");
|
||||||
@ -294,6 +294,7 @@ export async function connectReq(
|
|||||||
const role = opts?.role ?? "operator";
|
const role = opts?.role ?? "operator";
|
||||||
const requestedScopes = Array.isArray(opts?.scopes) ? opts?.scopes : [];
|
const requestedScopes = Array.isArray(opts?.scopes) ? opts?.scopes : [];
|
||||||
const device = (() => {
|
const device = (() => {
|
||||||
|
if (opts?.device === null) return undefined;
|
||||||
if (opts?.device) return opts.device;
|
if (opts?.device) return opts.device;
|
||||||
const identity = loadOrCreateDeviceIdentity();
|
const identity = loadOrCreateDeviceIdentity();
|
||||||
const signedAtMs = Date.now();
|
const signedAtMs = Date.now();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user