fix: WS upgrade auth — multi-token bearer not validated
Problem: isAuthenticated() for WS upgrade only checked legacy single token. iOS bearer token (from POST /pair → createToken()) was rejected → 403 on WS. Fix: - warmTokenCache(): pre-load all multi-tokens into a sync Set on startup - validateBearerSync(): O(1) sync lookup against the cache - createToken(): adds to cache immediately on creation - isAuthenticated(): checks validateBearerSync() as third fallback
This commit is contained in:
parent
38cad794e2
commit
b64aaab40a
|
|
@ -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<string>();
|
||||
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WsClient>();
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue