Files
platform/src/servers/api/browser/relay-auth.ts
T
pastilhasandClaude Opus 4.6 4d30672adb browser relay: multi-session override, stale session recovery, better error feedback
- relay accepts new extension connections by closing old one (last wins, code 4000)
- extension recognizes code 4000 and stops auto-reconnect (shows "replaced" badge)
- fix ping interval race where old WS close handler killed new connection's pings
- retry CDP commands on "Session with given id not found" by re-attaching debugger
- describeError() maps known errors to user-friendly badge tooltips
- persisted relay tokens restored on server start
- user-scoped token salts, token regeneration/deletion endpoints
- extension download endpoint, integrations UI for browser relay setup
- browser relay env vars passed to pi-bridge sandboxes
- browser screen layout with chat panel and prompt prefix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:22:15 +00:00

43 lines
1.2 KiB
TypeScript

import { createHmac } from 'node:crypto';
const RELAY_TOKEN_CONTEXT = 'officer-browser-relay-v1';
const { JWT_SECRET } = process.env;
if (!JWT_SECRET) {
throw new Error('JWT_SECRET is required for browser relay auth');
}
export function deriveRelayToken(userId: number, port: number, salt: string): string {
return createHmac('sha256', JWT_SECRET!)
.update(`${RELAY_TOKEN_CONTEXT}:${port}:${userId}:${salt}`)
.digest('hex');
}
const tokenToUser = new Map<string, number>();
export function registerUserToken(userId: number, port: number, salt: string): string {
const token = deriveRelayToken(userId, port, salt);
tokenToUser.set(token, userId);
return token;
}
export function unregisterUserToken(token: string): void {
tokenToUser.delete(token);
}
export function restoreUserTokens(entries: Array<{ userId: number; salt: string }>, port: number): void {
for (const { userId, salt } of entries) {
const token = deriveRelayToken(userId, port, salt);
tokenToUser.set(token, userId);
}
}
export function resolveUserFromToken(token: string): number | null {
return tokenToUser.get(token) ?? null;
}
export function getRelayAuthHeader(): string {
return 'x-officer-relay-token';
}