mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 18:04:32 +00:00
fix(gateway): clarify pairing and node auth guidance
This commit is contained in:
@@ -993,6 +993,42 @@ describe("gateway server auth/connect", () => {
|
||||
restoreGatewayToken(prevToken);
|
||||
});
|
||||
|
||||
test("keeps shared token mismatch reason when token fallback device-token check fails", async () => {
|
||||
const { server, ws, port, prevToken } = await startServerWithClient("secret");
|
||||
await ensurePairedDeviceTokenForCurrentIdentity(ws);
|
||||
|
||||
ws.close();
|
||||
|
||||
const ws2 = await openWs(port);
|
||||
const res2 = await connectReq(ws2, { token: "wrong" });
|
||||
expect(res2.ok).toBe(false);
|
||||
expect(res2.error?.message ?? "").toContain("gateway token mismatch");
|
||||
expect(res2.error?.message ?? "").not.toContain("device token mismatch");
|
||||
|
||||
ws2.close();
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
});
|
||||
|
||||
test("reports device token mismatch when explicit auth.deviceToken is wrong", async () => {
|
||||
const { server, ws, port, prevToken } = await startServerWithClient("secret");
|
||||
await ensurePairedDeviceTokenForCurrentIdentity(ws);
|
||||
|
||||
ws.close();
|
||||
|
||||
const ws2 = await openWs(port);
|
||||
const res2 = await connectReq(ws2, {
|
||||
skipDefaultAuth: true,
|
||||
deviceToken: "not-a-valid-device-token",
|
||||
});
|
||||
expect(res2.ok).toBe(false);
|
||||
expect(res2.error?.message ?? "").toContain("device token mismatch");
|
||||
|
||||
ws2.close();
|
||||
await server.close();
|
||||
restoreGatewayToken(prevToken);
|
||||
});
|
||||
|
||||
test("keeps shared-secret lockout separate from device-token auth", async () => {
|
||||
const { server, port, prevToken, deviceToken } =
|
||||
await startRateLimitedTokenServerWithPairedDeviceToken();
|
||||
|
||||
@@ -17,6 +17,8 @@ type HandshakeConnectAuth = {
|
||||
password?: string;
|
||||
};
|
||||
|
||||
export type DeviceTokenCandidateSource = "explicit-device-token" | "shared-token-fallback";
|
||||
|
||||
export type ConnectAuthState = {
|
||||
authResult: GatewayAuthResult;
|
||||
authOk: boolean;
|
||||
@@ -24,6 +26,7 @@ export type ConnectAuthState = {
|
||||
sharedAuthOk: boolean;
|
||||
sharedAuthProvided: boolean;
|
||||
deviceTokenCandidate?: string;
|
||||
deviceTokenCandidateSource?: DeviceTokenCandidateSource;
|
||||
};
|
||||
|
||||
function trimToUndefined(value: string | undefined): string | undefined {
|
||||
@@ -45,14 +48,19 @@ function resolveSharedConnectAuth(
|
||||
return { token, password };
|
||||
}
|
||||
|
||||
function resolveDeviceTokenCandidate(
|
||||
connectAuth: HandshakeConnectAuth | null | undefined,
|
||||
): string | undefined {
|
||||
function resolveDeviceTokenCandidate(connectAuth: HandshakeConnectAuth | null | undefined): {
|
||||
token?: string;
|
||||
source?: DeviceTokenCandidateSource;
|
||||
} {
|
||||
const explicitDeviceToken = trimToUndefined(connectAuth?.deviceToken);
|
||||
if (explicitDeviceToken) {
|
||||
return explicitDeviceToken;
|
||||
return { token: explicitDeviceToken, source: "explicit-device-token" };
|
||||
}
|
||||
return trimToUndefined(connectAuth?.token);
|
||||
const fallbackToken = trimToUndefined(connectAuth?.token);
|
||||
if (!fallbackToken) {
|
||||
return {};
|
||||
}
|
||||
return { token: fallbackToken, source: "shared-token-fallback" };
|
||||
}
|
||||
|
||||
export async function resolveConnectAuthState(params: {
|
||||
@@ -67,9 +75,8 @@ export async function resolveConnectAuthState(params: {
|
||||
}): Promise<ConnectAuthState> {
|
||||
const sharedConnectAuth = resolveSharedConnectAuth(params.connectAuth);
|
||||
const sharedAuthProvided = Boolean(sharedConnectAuth);
|
||||
const deviceTokenCandidate = params.hasDeviceIdentity
|
||||
? resolveDeviceTokenCandidate(params.connectAuth)
|
||||
: undefined;
|
||||
const { token: deviceTokenCandidate, source: deviceTokenCandidateSource } =
|
||||
params.hasDeviceIdentity ? resolveDeviceTokenCandidate(params.connectAuth) : {};
|
||||
const hasDeviceTokenCandidate = Boolean(deviceTokenCandidate);
|
||||
|
||||
let authResult: GatewayAuthResult = await authorizeWsControlUiGatewayConnect({
|
||||
@@ -129,5 +136,6 @@ export async function resolveConnectAuthState(params: {
|
||||
sharedAuthOk,
|
||||
sharedAuthProvided,
|
||||
deviceTokenCandidate,
|
||||
deviceTokenCandidateSource,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -355,17 +355,23 @@ export function attachGatewayWsMessageHandler(params: {
|
||||
});
|
||||
const device = controlUiAuthPolicy.device;
|
||||
|
||||
let { authResult, authOk, authMethod, sharedAuthOk, deviceTokenCandidate } =
|
||||
await resolveConnectAuthState({
|
||||
resolvedAuth,
|
||||
connectAuth: connectParams.auth,
|
||||
hasDeviceIdentity: Boolean(device),
|
||||
req: upgradeReq,
|
||||
trustedProxies,
|
||||
allowRealIpFallback,
|
||||
rateLimiter,
|
||||
clientIp,
|
||||
});
|
||||
let {
|
||||
authResult,
|
||||
authOk,
|
||||
authMethod,
|
||||
sharedAuthOk,
|
||||
deviceTokenCandidate,
|
||||
deviceTokenCandidateSource,
|
||||
} = await resolveConnectAuthState({
|
||||
resolvedAuth,
|
||||
connectAuth: connectParams.auth,
|
||||
hasDeviceIdentity: Boolean(device),
|
||||
req: upgradeReq,
|
||||
trustedProxies,
|
||||
allowRealIpFallback,
|
||||
rateLimiter,
|
||||
clientIp,
|
||||
});
|
||||
const rejectUnauthorized = (failedAuth: GatewayAuthResult) => {
|
||||
markHandshakeFailure("unauthorized", {
|
||||
authMode: resolvedAuth.mode,
|
||||
@@ -532,7 +538,11 @@ export function attachGatewayWsMessageHandler(params: {
|
||||
authMethod = "device-token";
|
||||
rateLimiter?.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
|
||||
} else {
|
||||
authResult = { ok: false, reason: "device_token_mismatch" };
|
||||
const mismatchReason =
|
||||
deviceTokenCandidateSource === "explicit-device-token"
|
||||
? "device_token_mismatch"
|
||||
: (authResult.reason ?? "device_token_mismatch");
|
||||
authResult = { ok: false, reason: mismatchReason };
|
||||
rateLimiter?.recordFailure(clientIp, AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user