import { serviceFetch } from './http' import { jwtManager } from './manager' import { type JwtManager, JwtManagerError } from './types' export interface SocketOptions { issuer: string url: string manager?: JwtManager } function issuerUrl(raw: string): string { const url = new URL(raw) if ( url.origin !== raw || url.username || url.password || (url.protocol !== 'https:' && !(url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname))) ) { throw new JwtManagerError('issuer_invalid', 'Issuer must be an HTTPS origin or loopback HTTP origin') } return raw } export async function connectSocket(options: SocketOptions): Promise { const issuer = issuerUrl(options.issuer) const url = new URL(options.url, location.href) if ( url.protocol !== 'wss:' && !(url.protocol === 'ws:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) ) { throw new JwtManagerError('socket_url_invalid', 'Socket URL requires WSS or loopback WS') } const response = await serviceFetch('auth', `${issuer}/api/v1/oauth/socket-tickets`, { baseUrl: issuer, method: 'POST', manager: options.manager ?? jwtManager, credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ origin: location.origin }), signal: AbortSignal.timeout(10_000), }) const body = (await response.json()) as { ticket?: string } if (!response.ok || !body.ticket) throw new JwtManagerError('socket_ticket_failed', 'Socket ticket issuance failed') url.searchParams.set('ticket', body.ticket) return new WebSocket(url) }