mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 22:44:31 +00:00
refactor(infra): dedupe device pairing token updates
This commit is contained in:
@@ -179,6 +179,35 @@ function newToken() {
|
|||||||
return generatePairingToken();
|
return generatePairingToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPairedDeviceFromState(
|
||||||
|
state: DevicePairingStateFile,
|
||||||
|
deviceId: string,
|
||||||
|
): PairedDevice | null {
|
||||||
|
return state.pairedByDeviceId[normalizeDeviceId(deviceId)] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneDeviceTokens(device: PairedDevice): Record<string, DeviceAuthToken> {
|
||||||
|
return device.tokens ? { ...device.tokens } : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDeviceAuthToken(params: {
|
||||||
|
role: string;
|
||||||
|
scopes: string[];
|
||||||
|
existing?: DeviceAuthToken;
|
||||||
|
now: number;
|
||||||
|
rotatedAtMs?: number;
|
||||||
|
}): DeviceAuthToken {
|
||||||
|
return {
|
||||||
|
token: newToken(),
|
||||||
|
role: params.role,
|
||||||
|
scopes: params.scopes,
|
||||||
|
createdAtMs: params.existing?.createdAtMs ?? params.now,
|
||||||
|
rotatedAtMs: params.rotatedAtMs,
|
||||||
|
revokedAtMs: undefined,
|
||||||
|
lastUsedAtMs: params.existing?.lastUsedAtMs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function listDevicePairing(baseDir?: string): Promise<DevicePairingList> {
|
export async function listDevicePairing(baseDir?: string): Promise<DevicePairingList> {
|
||||||
const state = await loadState(baseDir);
|
const state = await loadState(baseDir);
|
||||||
const pending = Object.values(state.pendingById).toSorted((a, b) => b.ts - a.ts);
|
const pending = Object.values(state.pendingById).toSorted((a, b) => b.ts - a.ts);
|
||||||
@@ -360,7 +389,7 @@ export async function verifyDeviceToken(params: {
|
|||||||
}): Promise<{ ok: boolean; reason?: string }> {
|
}): Promise<{ ok: boolean; reason?: string }> {
|
||||||
return await withLock(async () => {
|
return await withLock(async () => {
|
||||||
const state = await loadState(params.baseDir);
|
const state = await loadState(params.baseDir);
|
||||||
const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
|
const device = getPairedDeviceFromState(state, params.deviceId);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return { ok: false, reason: "device-not-paired" };
|
return { ok: false, reason: "device-not-paired" };
|
||||||
}
|
}
|
||||||
@@ -399,7 +428,7 @@ export async function ensureDeviceToken(params: {
|
|||||||
}): Promise<DeviceAuthToken | null> {
|
}): Promise<DeviceAuthToken | null> {
|
||||||
return await withLock(async () => {
|
return await withLock(async () => {
|
||||||
const state = await loadState(params.baseDir);
|
const state = await loadState(params.baseDir);
|
||||||
const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
|
const device = getPairedDeviceFromState(state, params.deviceId);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -408,7 +437,7 @@ export async function ensureDeviceToken(params: {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const requestedScopes = normalizeScopes(params.scopes);
|
const requestedScopes = normalizeScopes(params.scopes);
|
||||||
const tokens = device.tokens ? { ...device.tokens } : {};
|
const tokens = cloneDeviceTokens(device);
|
||||||
const existing = tokens[role];
|
const existing = tokens[role];
|
||||||
if (existing && !existing.revokedAtMs) {
|
if (existing && !existing.revokedAtMs) {
|
||||||
if (scopesAllow(requestedScopes, existing.scopes)) {
|
if (scopesAllow(requestedScopes, existing.scopes)) {
|
||||||
@@ -416,15 +445,13 @@ export async function ensureDeviceToken(params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const next: DeviceAuthToken = {
|
const next = buildDeviceAuthToken({
|
||||||
token: newToken(),
|
|
||||||
role,
|
role,
|
||||||
scopes: requestedScopes,
|
scopes: requestedScopes,
|
||||||
createdAtMs: existing?.createdAtMs ?? now,
|
existing,
|
||||||
|
now,
|
||||||
rotatedAtMs: existing ? now : undefined,
|
rotatedAtMs: existing ? now : undefined,
|
||||||
revokedAtMs: undefined,
|
});
|
||||||
lastUsedAtMs: existing?.lastUsedAtMs,
|
|
||||||
};
|
|
||||||
tokens[role] = next;
|
tokens[role] = next;
|
||||||
device.tokens = tokens;
|
device.tokens = tokens;
|
||||||
state.pairedByDeviceId[device.deviceId] = device;
|
state.pairedByDeviceId[device.deviceId] = device;
|
||||||
@@ -441,7 +468,7 @@ export async function rotateDeviceToken(params: {
|
|||||||
}): Promise<DeviceAuthToken | null> {
|
}): Promise<DeviceAuthToken | null> {
|
||||||
return await withLock(async () => {
|
return await withLock(async () => {
|
||||||
const state = await loadState(params.baseDir);
|
const state = await loadState(params.baseDir);
|
||||||
const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
|
const device = getPairedDeviceFromState(state, params.deviceId);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -449,19 +476,17 @@ export async function rotateDeviceToken(params: {
|
|||||||
if (!role) {
|
if (!role) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const tokens = device.tokens ? { ...device.tokens } : {};
|
const tokens = cloneDeviceTokens(device);
|
||||||
const existing = tokens[role];
|
const existing = tokens[role];
|
||||||
const requestedScopes = normalizeScopes(params.scopes ?? existing?.scopes ?? device.scopes);
|
const requestedScopes = normalizeScopes(params.scopes ?? existing?.scopes ?? device.scopes);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const next: DeviceAuthToken = {
|
const next = buildDeviceAuthToken({
|
||||||
token: newToken(),
|
|
||||||
role,
|
role,
|
||||||
scopes: requestedScopes,
|
scopes: requestedScopes,
|
||||||
createdAtMs: existing?.createdAtMs ?? now,
|
existing,
|
||||||
|
now,
|
||||||
rotatedAtMs: now,
|
rotatedAtMs: now,
|
||||||
revokedAtMs: undefined,
|
});
|
||||||
lastUsedAtMs: existing?.lastUsedAtMs,
|
|
||||||
};
|
|
||||||
tokens[role] = next;
|
tokens[role] = next;
|
||||||
device.tokens = tokens;
|
device.tokens = tokens;
|
||||||
if (params.scopes !== undefined) {
|
if (params.scopes !== undefined) {
|
||||||
|
|||||||
Reference in New Issue
Block a user