diff --git a/packages/streamwall-control-server/src/auth.ts b/packages/streamwall-control-server/src/auth.ts index fb2e0e0..eaeb808 100644 --- a/packages/streamwall-control-server/src/auth.ts +++ b/packages/streamwall-control-server/src/auth.ts @@ -29,10 +29,19 @@ const base62 = baseX( '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ) -function rand62(len: number) { +export function rand62(len: number) { return base62.encode(randomBytes(len)) } +export function uniqueRand62(len: number, map: Map) { + let val = rand62(len) + while (map.has(val)) { + // Regenerate in case of a collision + val = rand62(len) + } + return val +} + async function hashToken62(token: string, salt: string) { const hashBuffer = await scrypt(token, salt, 24) return base62.encode(hashBuffer as Buffer) @@ -162,12 +171,7 @@ export class Auth extends EventEmitter { throw new Error(`invalid role: ${role}`) } - let tokenId = rand62(8) - while (this.tokensById.has(tokenId)) { - // Regenerate in case of an id collision - tokenId = rand62(8) - } - + const tokenId = uniqueRand62(8, this.tokensById) const secret = rand62(24) const tokenHash = await hashToken62(secret, this.salt) const tokenData = { diff --git a/packages/streamwall-control-server/src/index.ts b/packages/streamwall-control-server/src/index.ts index 27a76bc..7952819 100644 --- a/packages/streamwall-control-server/src/index.ts +++ b/packages/streamwall-control-server/src/index.ts @@ -16,12 +16,13 @@ import { stateDiff, type StreamwallRole, } from 'streamwall-shared' -import { Auth, StateWrapper } from './auth.ts' +import { Auth, StateWrapper, uniqueRand62 } from './auth.ts' import { loadStorage, type StorageDB } from './storage.ts' export const SESSION_COOKIE_NAME = 's' interface Client { + clientId: string ws: WebSocket lastStateSent: any identity: AuthTokenInfo @@ -248,7 +249,7 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) { console.error('Failed to send Streamwall doc update') } for (const client of clients.values()) { - if (client.identity.tokenId === origin) { + if (client.clientId === origin) { continue } try { @@ -299,25 +300,39 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) { return } + const clientId = uniqueRand62(8, clients) const client: Client = { + clientId, ws, lastStateSent: null, identity, } - clients.set(identity.tokenId, client) + clients.set(clientId, client) const pingInterval = setInterval(() => { ws.ping() }, 20 * 1000) ws.on('close', () => { - clients.delete(identity.tokenId) + clients.delete(clientId) clearInterval(pingInterval) - console.log('Client disconnected from', request.ip, client.identity) + console.log( + 'Client', + clientId, + 'disconnected from', + request.ip, + client.identity, + ) }) - console.log('Client connected from', request.ip, client.identity) + console.log( + 'Client', + clientId, + 'connected from', + request.ip, + client.identity, + ) handleMessage(async (rawData) => { let msg: ControlCommandMessage @@ -350,7 +365,7 @@ async function initApp({ baseURL, clientStaticPath }: AppOptions) { Y.applyUpdate( streamwallConn.stateDoc, new Uint8Array(rawData), - identity.tokenId, + clientId, ) return }