mirror of https://github.com/openclaw/openclaw.git
refactor: share handshake auth helper builders
This commit is contained in:
parent
6ecc184637
commit
4674fbf923
|
|
@ -10,24 +10,21 @@ import {
|
||||||
shouldSkipBackendSelfPairing,
|
shouldSkipBackendSelfPairing,
|
||||||
} from "./handshake-auth-helpers.js";
|
} from "./handshake-auth-helpers.js";
|
||||||
|
|
||||||
|
function createRateLimiter(): AuthRateLimiter {
|
||||||
|
return {
|
||||||
|
check: () => ({ allowed: true, remaining: 1, retryAfterMs: 0 }),
|
||||||
|
reset: () => {},
|
||||||
|
recordFailure: () => {},
|
||||||
|
size: () => 0,
|
||||||
|
prune: () => {},
|
||||||
|
dispose: () => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("handshake auth helpers", () => {
|
describe("handshake auth helpers", () => {
|
||||||
it("pins browser-origin loopback clients to the synthetic rate-limit ip", () => {
|
it("pins browser-origin loopback clients to the synthetic rate-limit ip", () => {
|
||||||
const rateLimiter: AuthRateLimiter = {
|
const rateLimiter = createRateLimiter();
|
||||||
check: () => ({ allowed: true, remaining: 1, retryAfterMs: 0 }),
|
const browserRateLimiter = createRateLimiter();
|
||||||
reset: () => {},
|
|
||||||
recordFailure: () => {},
|
|
||||||
size: () => 0,
|
|
||||||
prune: () => {},
|
|
||||||
dispose: () => {},
|
|
||||||
};
|
|
||||||
const browserRateLimiter: AuthRateLimiter = {
|
|
||||||
check: () => ({ allowed: true, remaining: 1, retryAfterMs: 0 }),
|
|
||||||
reset: () => {},
|
|
||||||
recordFailure: () => {},
|
|
||||||
size: () => 0,
|
|
||||||
prune: () => {},
|
|
||||||
dispose: () => {},
|
|
||||||
};
|
|
||||||
const resolved = resolveHandshakeBrowserSecurityContext({
|
const resolved = resolveHandshakeBrowserSecurityContext({
|
||||||
requestOrigin: "https://app.example",
|
requestOrigin: "https://app.example",
|
||||||
clientIp: "127.0.0.1",
|
clientIp: "127.0.0.1",
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,23 @@ function resolveSignatureToken(connectParams: ConnectParams): string | null {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildUnauthorizedHandshakeContext(params: {
|
||||||
|
authProvided: AuthProvidedKind;
|
||||||
|
canRetryWithDeviceToken: boolean;
|
||||||
|
recommendedNextStep:
|
||||||
|
| "retry_with_device_token"
|
||||||
|
| "update_auth_configuration"
|
||||||
|
| "update_auth_credentials"
|
||||||
|
| "wait_then_retry"
|
||||||
|
| "review_auth_configuration";
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
authProvided: params.authProvided,
|
||||||
|
canRetryWithDeviceToken: params.canRetryWithDeviceToken,
|
||||||
|
recommendedNextStep: params.recommendedNextStep,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveDeviceSignaturePayloadVersion(params: {
|
export function resolveDeviceSignaturePayloadVersion(params: {
|
||||||
device: {
|
device: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -104,7 +121,7 @@ export function resolveDeviceSignaturePayloadVersion(params: {
|
||||||
nonce: string;
|
nonce: string;
|
||||||
}): "v3" | "v2" | null {
|
}): "v3" | "v2" | null {
|
||||||
const signatureToken = resolveSignatureToken(params.connectParams);
|
const signatureToken = resolveSignatureToken(params.connectParams);
|
||||||
const payloadV3 = buildDeviceAuthPayloadV3({
|
const basePayload = {
|
||||||
deviceId: params.device.id,
|
deviceId: params.device.id,
|
||||||
clientId: params.connectParams.client.id,
|
clientId: params.connectParams.client.id,
|
||||||
clientMode: params.connectParams.client.mode,
|
clientMode: params.connectParams.client.mode,
|
||||||
|
|
@ -113,6 +130,9 @@ export function resolveDeviceSignaturePayloadVersion(params: {
|
||||||
signedAtMs: params.signedAtMs,
|
signedAtMs: params.signedAtMs,
|
||||||
token: signatureToken,
|
token: signatureToken,
|
||||||
nonce: params.nonce,
|
nonce: params.nonce,
|
||||||
|
};
|
||||||
|
const payloadV3 = buildDeviceAuthPayloadV3({
|
||||||
|
...basePayload,
|
||||||
platform: params.connectParams.client.platform,
|
platform: params.connectParams.client.platform,
|
||||||
deviceFamily: params.connectParams.client.deviceFamily,
|
deviceFamily: params.connectParams.client.deviceFamily,
|
||||||
});
|
});
|
||||||
|
|
@ -120,16 +140,7 @@ export function resolveDeviceSignaturePayloadVersion(params: {
|
||||||
return "v3";
|
return "v3";
|
||||||
}
|
}
|
||||||
|
|
||||||
const payloadV2 = buildDeviceAuthPayload({
|
const payloadV2 = buildDeviceAuthPayload(basePayload);
|
||||||
deviceId: params.device.id,
|
|
||||||
clientId: params.connectParams.client.id,
|
|
||||||
clientMode: params.connectParams.client.mode,
|
|
||||||
role: params.role,
|
|
||||||
scopes: params.scopes,
|
|
||||||
signedAtMs: params.signedAtMs,
|
|
||||||
token: signatureToken,
|
|
||||||
nonce: params.nonce,
|
|
||||||
});
|
|
||||||
if (verifyDeviceSignature(params.device.publicKey, payloadV2, params.device.signature)) {
|
if (verifyDeviceSignature(params.device.publicKey, payloadV2, params.device.signature)) {
|
||||||
return "v2";
|
return "v2";
|
||||||
}
|
}
|
||||||
|
|
@ -171,41 +182,41 @@ export function resolveUnauthorizedHandshakeContext(params: {
|
||||||
authProvided === "token" &&
|
authProvided === "token" &&
|
||||||
!params.connectAuth?.deviceToken;
|
!params.connectAuth?.deviceToken;
|
||||||
if (canRetryWithDeviceToken) {
|
if (canRetryWithDeviceToken) {
|
||||||
return {
|
return buildUnauthorizedHandshakeContext({
|
||||||
authProvided,
|
authProvided,
|
||||||
canRetryWithDeviceToken,
|
canRetryWithDeviceToken,
|
||||||
recommendedNextStep: "retry_with_device_token",
|
recommendedNextStep: "retry_with_device_token",
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
switch (params.failedAuth.reason) {
|
switch (params.failedAuth.reason) {
|
||||||
case "token_missing":
|
case "token_missing":
|
||||||
case "token_missing_config":
|
case "token_missing_config":
|
||||||
case "password_missing":
|
case "password_missing":
|
||||||
case "password_missing_config":
|
case "password_missing_config":
|
||||||
return {
|
return buildUnauthorizedHandshakeContext({
|
||||||
authProvided,
|
authProvided,
|
||||||
canRetryWithDeviceToken,
|
canRetryWithDeviceToken,
|
||||||
recommendedNextStep: "update_auth_configuration",
|
recommendedNextStep: "update_auth_configuration",
|
||||||
};
|
});
|
||||||
case "token_mismatch":
|
case "token_mismatch":
|
||||||
case "password_mismatch":
|
case "password_mismatch":
|
||||||
case "device_token_mismatch":
|
case "device_token_mismatch":
|
||||||
return {
|
return buildUnauthorizedHandshakeContext({
|
||||||
authProvided,
|
authProvided,
|
||||||
canRetryWithDeviceToken,
|
canRetryWithDeviceToken,
|
||||||
recommendedNextStep: "update_auth_credentials",
|
recommendedNextStep: "update_auth_credentials",
|
||||||
};
|
});
|
||||||
case "rate_limited":
|
case "rate_limited":
|
||||||
return {
|
return buildUnauthorizedHandshakeContext({
|
||||||
authProvided,
|
authProvided,
|
||||||
canRetryWithDeviceToken,
|
canRetryWithDeviceToken,
|
||||||
recommendedNextStep: "wait_then_retry",
|
recommendedNextStep: "wait_then_retry",
|
||||||
};
|
});
|
||||||
default:
|
default:
|
||||||
return {
|
return buildUnauthorizedHandshakeContext({
|
||||||
authProvided,
|
authProvided,
|
||||||
canRetryWithDeviceToken,
|
canRetryWithDeviceToken,
|
||||||
recommendedNextStep: "review_auth_configuration",
|
recommendedNextStep: "review_auth_configuration",
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue