| 152 | |
| 153 | /** Extract auth token from WS upgrade request and build headers object with synthetic Authorization */ |
| 154 | export function extractWsHeaders(req: IncomingMessage): IncomingHttpHeaders { |
| 155 | // Start with actual headers |
| 156 | const headers = { ...req.headers }; |
| 157 | |
| 158 | // If no Authorization header, try fallback methods |
| 159 | if (!headers.authorization) { |
| 160 | // 1) Query param: ?token=... |
| 161 | try { |
| 162 | const url = new URL(req.url ?? "", "http://localhost"); |
| 163 | const qp = url.searchParams.get("token"); |
| 164 | if (qp?.trim()) { |
| 165 | headers.authorization = `Bearer ${qp.trim()}`; |
| 166 | return headers; |
| 167 | } |
| 168 | } catch { |
| 169 | /* ignore */ |
| 170 | } |
| 171 | |
| 172 | // 2) Sec-WebSocket-Protocol (first value as token) |
| 173 | const proto = req.headers["sec-websocket-protocol"]; |
| 174 | if (typeof proto === "string") { |
| 175 | const first = proto |
| 176 | .split(",") |
| 177 | .map((s) => s.trim()) |
| 178 | .find((s) => s); |
| 179 | if (first) { |
| 180 | headers.authorization = `Bearer ${first}`; |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | return headers; |
| 186 | } |