fix: embed token in HTML for WebSocket auth

The WebSocket connection at /ws was not including the token parameter,
relying solely on the session cookie from the initial redirect. When
scanning the QR code on a phone, the token (longest part of URL) was
being truncated, so no cookie was ever set and all requests got 403.

Fix: embed the token directly into the page as a JS variable, and
append it to the WebSocket connection URL as a fallback. Now both
the HTTP page and the WebSocket upgrade work even if the cookie
isn't available.
This commit is contained in:
Marc 2026-04-12 05:25:20 -06:00
parent a0713e8a02
commit 84e0caa1d3
2 changed files with 9 additions and 4 deletions

View File

@ -5,7 +5,7 @@
* Everything is self-contained no external dependencies.
*/
export function buildHTML(nonce: string): string {
export function buildHTML(nonce: string, token?: string): string {
return /* html */ `<!DOCTYPE html>
<html lang="en">
<head>
@ -398,6 +398,7 @@ return /* html */ `<!DOCTYPE html>
</div>
</div>
<script nonce="${nonce}">
(function(){var _rcToken="${token || ''}";})();
(function () {
"use strict";
@ -605,7 +606,11 @@ return /* html */ `<!DOCTYPE html>
var ws, timer;
function connect() {
var wsProtocol = location.protocol === "https:" ? "wss:" : "ws:";
ws = new WebSocket(wsProtocol + "//" + location.host + "/ws");
var wsUrl = wsProtocol + "//" + location.host + "/ws";
if (typeof _rcToken !== "undefined" && _rcToken) {
wsUrl += "?token=" + encodeURIComponent(_rcToken);
}
ws = new WebSocket(wsUrl);
ws.onopen = function () {
clearTimeout(timer);

View File

@ -126,7 +126,7 @@ export function startServer(pi: ExtensionAPI, ctx: ExtensionContext): Promise<Re
"Content-Security-Policy":
`default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; base-uri 'none'`,
});
res.end(buildHTML(nonce));
res.end(buildHTML(nonce, token));
} else {
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
res.end("Not found");
@ -350,7 +350,7 @@ export function startServerTailscale(pi: ExtensionAPI, ctx: ExtensionContext): P
"Content-Security-Policy":
`default-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}'; connect-src 'self'; base-uri 'none'`,
});
res.end(buildHTML(nonce));
res.end(buildHTML(nonce, token));
} else {
res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
res.end("Not found");