import { createHash, timingSafeEqual } from "node:crypto"; const WS_AUTH_PROTOCOL_PREFIX = "rcs.auth."; function sha256(value: string): Buffer { return createHash("sha256").update(value).digest(); } export function encodeWebSocketAuthProtocol(token: string): string { return `${WS_AUTH_PROTOCOL_PREFIX}${Buffer.from(token, "utf8").toString("base64url")}`; } export function decodeWebSocketAuthProtocol(protocolHeader: string | undefined): string | undefined { if (!protocolHeader) { return undefined; } for (const protocol of protocolHeader.split(",")) { const trimmed = protocol.trim(); if (!trimmed.startsWith(WS_AUTH_PROTOCOL_PREFIX)) { continue; } const encoded = trimmed.slice(WS_AUTH_PROTOCOL_PREFIX.length); if (!encoded) { return undefined; } try { const token = Buffer.from(encoded, "base64url").toString("utf8"); return token.length > 0 ? token : undefined; } catch { return undefined; } } return undefined; } export function extractBearerToken(authorizationHeader: string | undefined): string | undefined { return authorizationHeader?.startsWith("Bearer ") ? authorizationHeader.slice("Bearer ".length) : undefined; } export function extractWebSocketAuthToken(headers: { authorization?: string; protocol?: string; }): string | undefined { return extractBearerToken(headers.authorization) ?? decodeWebSocketAuthProtocol(headers.protocol); } export function authTokensEqual( providedToken: string | undefined, expectedToken: string | undefined, ): boolean { if (!providedToken || !expectedToken) { return false; } return timingSafeEqual(sha256(providedToken), sha256(expectedToken)); }