diff --git a/extensions/remote-control/auth/tokens.ts b/extensions/remote-control/auth/tokens.ts index e75bdf2..a793257 100644 --- a/extensions/remote-control/auth/tokens.ts +++ b/extensions/remote-control/auth/tokens.ts @@ -20,6 +20,29 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// --------------------------------------------------------------------------- +// In-memory cache for sync validation (WS upgrade can't await) +// --------------------------------------------------------------------------- + +const _tokenCache = new Set(); + +function cacheToken(token: string): void { + _tokenCache.add(token); +} + +/** Sync bearer validation — checks in-memory cache populated at runtime. */ +export function validateBearerSync(bearer: string): boolean { + return _tokenCache.has(bearer); +} + +/** Warm the cache from disk on startup. */ +export async function warmTokenCache(stateDir?: string): Promise { + const entries = await loadTokens(stateDir); + for (const e of entries) _tokenCache.add(e.token); +} + +// --------------------------------------------------------------------------- + export interface TokenEntry { id: string; token: string; @@ -70,6 +93,7 @@ export async function createToken( }; entries.push(entry); await saveTokens(entries, stateDir); + cacheToken(entry.token); return entry; } diff --git a/extensions/remote-control/server/server.ts b/extensions/remote-control/server/server.ts index 115b6d3..9e815fa 100644 --- a/extensions/remote-control/server/server.ts +++ b/extensions/remote-control/server/server.ts @@ -34,7 +34,12 @@ import { generatePairingToken, printPairingQr, } from "../auth/pairing.js"; -import { createToken, validateBearer } from "../auth/tokens.js"; +import { + createToken, + validateBearer, + validateBearerSync, + warmTokenCache, +} from "../auth/tokens.js"; import { generateSessionId, loadOrCreateToken, @@ -81,6 +86,7 @@ export async function startServer( const clientChangeListeners: Array<() => void> = []; const clients = new Set(); const token = await loadOrCreateToken(); + await warmTokenCache(); // pre-load multi-tokens into sync cache // Map of valid session IDs → expiry timestamp (ms since epoch) const SESSION_TTL_MS = 86_400_000; // 24 h — matches cookie Max-Age @@ -112,8 +118,7 @@ export async function startServer( const bearer = extractBearer(req); if (bearer) { if (validateToken(bearer, token)) return true; - // async validateBearer checked in asyncHandler for API routes; - // for WS upgrade we do a sync fallback: legacy token only here. + if (validateBearerSync(bearer)) return true; // multi-token sync cache } return false;