From f3a474af30bca9fe3359fc8071be0db7f432c70d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 04:22:39 +0000 Subject: [PATCH] refactor(device-auth): share store types + normalization --- src/infra/device-auth-store.ts | 45 ++++++++-------------------------- src/shared/device-auth.ts | 30 +++++++++++++++++++++++ ui/src/ui/device-auth.ts | 44 ++++++++------------------------- 3 files changed, 50 insertions(+), 69 deletions(-) create mode 100644 src/shared/device-auth.ts diff --git a/src/infra/device-auth-store.ts b/src/infra/device-auth-store.ts index 62a27c97af..537d044f15 100644 --- a/src/infra/device-auth-store.ts +++ b/src/infra/device-auth-store.ts @@ -1,19 +1,12 @@ import fs from "node:fs"; import path from "node:path"; import { resolveStateDir } from "../config/paths.js"; - -export type DeviceAuthEntry = { - token: string; - role: string; - scopes: string[]; - updatedAtMs: number; -}; - -type DeviceAuthStore = { - version: 1; - deviceId: string; - tokens: Record; -}; +import { + type DeviceAuthEntry, + type DeviceAuthStore, + normalizeDeviceAuthRole, + normalizeDeviceAuthScopes, +} from "../shared/device-auth.js"; const DEVICE_AUTH_FILE = "device-auth.json"; @@ -21,24 +14,6 @@ function resolveDeviceAuthPath(env: NodeJS.ProcessEnv = process.env): string { return path.join(resolveStateDir(env), "identity", DEVICE_AUTH_FILE); } -function normalizeRole(role: string): string { - return role.trim(); -} - -function normalizeScopes(scopes: string[] | undefined): string[] { - if (!Array.isArray(scopes)) { - return []; - } - const out = new Set(); - for (const scope of scopes) { - const trimmed = scope.trim(); - if (trimmed) { - out.add(trimmed); - } - } - return [...out].toSorted(); -} - function readStore(filePath: string): DeviceAuthStore | null { try { if (!fs.existsSync(filePath)) { @@ -81,7 +56,7 @@ export function loadDeviceAuthToken(params: { if (store.deviceId !== params.deviceId) { return null; } - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); const entry = store.tokens[role]; if (!entry || typeof entry.token !== "string") { return null; @@ -98,7 +73,7 @@ export function storeDeviceAuthToken(params: { }): DeviceAuthEntry { const filePath = resolveDeviceAuthPath(params.env); const existing = readStore(filePath); - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); const next: DeviceAuthStore = { version: 1, deviceId: params.deviceId, @@ -110,7 +85,7 @@ export function storeDeviceAuthToken(params: { const entry: DeviceAuthEntry = { token: params.token, role, - scopes: normalizeScopes(params.scopes), + scopes: normalizeDeviceAuthScopes(params.scopes), updatedAtMs: Date.now(), }; next.tokens[role] = entry; @@ -128,7 +103,7 @@ export function clearDeviceAuthToken(params: { if (!store || store.deviceId !== params.deviceId) { return; } - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); if (!store.tokens[role]) { return; } diff --git a/src/shared/device-auth.ts b/src/shared/device-auth.ts new file mode 100644 index 0000000000..d093be0124 --- /dev/null +++ b/src/shared/device-auth.ts @@ -0,0 +1,30 @@ +export type DeviceAuthEntry = { + token: string; + role: string; + scopes: string[]; + updatedAtMs: number; +}; + +export type DeviceAuthStore = { + version: 1; + deviceId: string; + tokens: Record; +}; + +export function normalizeDeviceAuthRole(role: string): string { + return role.trim(); +} + +export function normalizeDeviceAuthScopes(scopes: string[] | undefined): string[] { + if (!Array.isArray(scopes)) { + return []; + } + const out = new Set(); + for (const scope of scopes) { + const trimmed = scope.trim(); + if (trimmed) { + out.add(trimmed); + } + } + return [...out].toSorted(); +} diff --git a/ui/src/ui/device-auth.ts b/ui/src/ui/device-auth.ts index 97ace4962d..2f1bc9be2e 100644 --- a/ui/src/ui/device-auth.ts +++ b/ui/src/ui/device-auth.ts @@ -1,36 +1,12 @@ -export type DeviceAuthEntry = { - token: string; - role: string; - scopes: string[]; - updatedAtMs: number; -}; - -type DeviceAuthStore = { - version: 1; - deviceId: string; - tokens: Record; -}; +import { + type DeviceAuthEntry, + type DeviceAuthStore, + normalizeDeviceAuthRole, + normalizeDeviceAuthScopes, +} from "../../../src/shared/device-auth.js"; const STORAGE_KEY = "openclaw.device.auth.v1"; -function normalizeRole(role: string): string { - return role.trim(); -} - -function normalizeScopes(scopes: string[] | undefined): string[] { - if (!Array.isArray(scopes)) { - return []; - } - const out = new Set(); - for (const scope of scopes) { - const trimmed = scope.trim(); - if (trimmed) { - out.add(trimmed); - } - } - return [...out].toSorted(); -} - function readStore(): DeviceAuthStore | null { try { const raw = window.localStorage.getItem(STORAGE_KEY); @@ -69,7 +45,7 @@ export function loadDeviceAuthToken(params: { if (!store || store.deviceId !== params.deviceId) { return null; } - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); const entry = store.tokens[role]; if (!entry || typeof entry.token !== "string") { return null; @@ -83,7 +59,7 @@ export function storeDeviceAuthToken(params: { token: string; scopes?: string[]; }): DeviceAuthEntry { - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); const next: DeviceAuthStore = { version: 1, deviceId: params.deviceId, @@ -96,7 +72,7 @@ export function storeDeviceAuthToken(params: { const entry: DeviceAuthEntry = { token: params.token, role, - scopes: normalizeScopes(params.scopes), + scopes: normalizeDeviceAuthScopes(params.scopes), updatedAtMs: Date.now(), }; next.tokens[role] = entry; @@ -109,7 +85,7 @@ export function clearDeviceAuthToken(params: { deviceId: string; role: string }) if (!store || store.deviceId !== params.deviceId) { return; } - const role = normalizeRole(params.role); + const role = normalizeDeviceAuthRole(params.role); if (!store.tokens[role]) { return; }