UNPKG

@bsv/authsocket-client

Version:
128 lines (114 loc) 4.04 kB
import { io as realIo, Socket as IoClientSocket, ManagerOptions, SocketOptions } from 'socket.io-client' import { RequestedCertificateSet, SessionManager, Peer, WalletInterface, Utils } from '@bsv/sdk' import { SocketClientTransport } from './SocketClientTransport.js' /** * Internal class that wraps a Socket.IO client connection with BRC-103 mutual authentication, * enabling secure and identity-aware communication with a server. */ class AuthSocketClientImpl { public connected = false public id: string = '' public serverIdentityKey: string | undefined private eventCallbacks = new Map<string, Array<(data: any) => void>>() /** * Creates an instance of AuthSocketClient. * * @param ioSocket - The underlying Socket.IO client socket instance. * @param peer - The BRC-103 Peer instance responsible for managing authenticated * communication, including message signing and verification. */ constructor( private ioSocket: IoClientSocket, private peer: Peer ) { // Listen for 'connect' and 'disconnect' from underlying Socket.IO this.ioSocket.on('connect', () => { this.connected = true this.id = this.ioSocket.id || '' // Re-dispatch to dev if they've called "socket.on('connect', ...)" this.fireEventCallbacks('connect') }) this.ioSocket.on('disconnect', (reason) => { this.connected = false // Re-dispatch this.fireEventCallbacks('disconnect', reason) }) // Also listen for BRC-103 "general" messages // We'll rely on peer.listenForGeneralMessages this.peer.listenForGeneralMessages((senderKey, payload) => { this.serverIdentityKey = senderKey const { eventName, data } = this.decodeEventPayload(payload) this.fireEventCallbacks(eventName, data) }) } on(eventName: string, callback: (data?: any) => void): this { let arr = this.eventCallbacks.get(eventName) if (!arr) { arr = [] this.eventCallbacks.set(eventName, arr) } arr.push(callback) return this } emit(eventName: string, data: any): this { // We sign a BRC-103 "general" message and send to the server // via peer.toPeer const encoded = this.encodeEventPayload(eventName, data) this.peer.toPeer(encoded, this.serverIdentityKey).catch(err => { console.error(`BRC103IoClientSocket emit error for event "${eventName}":`, err) }) return this } disconnect(): void { this.serverIdentityKey = undefined this.ioSocket.disconnect() } private fireEventCallbacks(eventName: string, data?: any) { const cbs = this.eventCallbacks.get(eventName) if (!cbs) return for (const cb of cbs) { cb(data) } } private encodeEventPayload(eventName: string, data: any): number[] { const obj = { eventName, data } return Utils.toArray(JSON.stringify(obj), 'utf8') } private decodeEventPayload(payload: number[]): { eventName: string, data: any } { try { const str = Utils.toUTF8(payload) return JSON.parse(str) } catch { return { eventName: '_unknown', data: undefined } } } } /** * Factory function for creating a new AuthSocketClientImpl instance. * * @param url - The server URL * @param opts - Contains wallet, requested certificates, and other optional settings */ export function AuthSocketClient( url: string, opts: { wallet: WalletInterface requestedCertificates?: RequestedCertificateSet sessionManager?: SessionManager managerOptions?: Partial<ManagerOptions & SocketOptions> } ): AuthSocketClientImpl { // 1) Create real socket.io-client connection const socket = realIo(url, opts.managerOptions) // 2) Create a BRC-103 transport for the new socket const transport = new SocketClientTransport(socket) // 3) Create a Peer const peer = new Peer( opts.wallet, transport, opts.requestedCertificates, opts.sessionManager ) // 4) Return our new AuthSocketClientImpl return new AuthSocketClientImpl(socket, peer) }