allow secure OpenWebRX follow-up requests after ticket auth

This commit is contained in:
2026-04-02 23:22:44 +02:00
parent 3ca8db4231
commit 6e3a363899

View File

@@ -5547,6 +5547,12 @@ async function handleOpenWebRxAuthorize(req, res, url) {
tickets.push(refererTicket); tickets.push(refererTicket);
} }
if (tickets.length === 0) { if (tickets.length === 0) {
if (canAuthorizeOpenWebRxFollowupWithoutTicket(req, originalUriHeader, refererHeader)) {
await ensureOpenWebRxSdrPath(null, { force: false, minIntervalMs: 3000 });
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
res.end("ok");
return;
}
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" }); res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
res.end("forbidden"); res.end("forbidden");
return; return;
@@ -5574,7 +5580,7 @@ async function handleOpenWebRxAuthorize(req, res, url) {
result.ok && result.ok &&
runtime.station.isInUse && runtime.station.isInUse &&
ownerUserId && ownerUserId &&
result.userId === ownerUserId String(result.userId) === String(ownerUserId)
); );
if (!allowed) { if (!allowed) {
res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" }); res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
@@ -5582,6 +5588,9 @@ async function handleOpenWebRxAuthorize(req, res, url) {
return; return;
} }
markOpenWebRxSession({ id: result.userId }, result); markOpenWebRxSession({ id: result.userId }, result);
const fingerprint = buildOpenWebRxRequesterFingerprint(req);
runtime.openWebRxSession.lastAuthorizedIp = fingerprint.ip;
runtime.openWebRxSession.lastAuthorizedUserAgent = fingerprint.userAgent;
await ensureOpenWebRxSdrPath(null, { force: false, minIntervalMs: 3000 }); await ensureOpenWebRxSdrPath(null, { force: false, minIntervalMs: 3000 });
res.writeHead(200, { res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8", "Content-Type": "text/plain; charset=utf-8",
@@ -5594,6 +5603,46 @@ async function handleOpenWebRxAuthorize(req, res, url) {
} }
} }
function canAuthorizeOpenWebRxFollowupWithoutTicket(req, originalUriHeader, refererHeader) {
if (!runtime.station || !runtime.station.isInUse || !runtime.station.activeByUserId) {
return false;
}
if (!isOpenWebRxSessionActive()) {
return false;
}
const sessionOwner = String(runtime.openWebRxSession && runtime.openWebRxSession.activeOwnerUserId ? runtime.openWebRxSession.activeOwnerUserId : "");
const stationOwner = String(runtime.station.activeByUserId || "");
if (!sessionOwner || !stationOwner || sessionOwner !== stationOwner) {
return false;
}
const fingerprint = buildOpenWebRxRequesterFingerprint(req);
const expectedIp = String(runtime.openWebRxSession && runtime.openWebRxSession.lastAuthorizedIp ? runtime.openWebRxSession.lastAuthorizedIp : "");
const expectedUserAgent = String(runtime.openWebRxSession && runtime.openWebRxSession.lastAuthorizedUserAgent ? runtime.openWebRxSession.lastAuthorizedUserAgent : "");
if (!expectedIp || !expectedUserAgent) {
return false;
}
if (!fingerprint.ip || !fingerprint.userAgent) {
return false;
}
if (fingerprint.ip !== expectedIp || fingerprint.userAgent !== expectedUserAgent) {
return false;
}
const openWebRxPath = String(config.openWebRxPath || "/sdr/").trim() || "/sdr/";
const originalUri = String(originalUriHeader || "");
const referer = String(refererHeader || "");
return originalUri.includes(openWebRxPath) || referer.includes(openWebRxPath);
}
function buildOpenWebRxRequesterFingerprint(req) {
const headers = req && req.headers ? req.headers : {};
const xForwardedFor = String(headers["x-forwarded-for"] || "").trim();
const ip = xForwardedFor ? xForwardedFor.split(",")[0].trim() : String(req && req.socket ? req.socket.remoteAddress || "" : "").trim();
const userAgent = String(headers["user-agent"] || "").trim();
return { ip, userAgent };
}
function readCookie(cookieHeader, name) { function readCookie(cookieHeader, name) {
const target = String(name || "").trim(); const target = String(name || "").trim();
if (!target) { if (!target) {
@@ -6997,6 +7046,8 @@ function markOpenWebRxSession(user, sessionResult) {
runtime.openWebRxSession = { runtime.openWebRxSession = {
activeOwnerUserId: ownerUserId || null, activeOwnerUserId: ownerUserId || null,
expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : (Date.now() + 120000), expiresAtMs: Number.isFinite(expiresAtMs) ? expiresAtMs : (Date.now() + 120000),
lastAuthorizedIp: runtime.openWebRxSession && runtime.openWebRxSession.lastAuthorizedIp ? runtime.openWebRxSession.lastAuthorizedIp : null,
lastAuthorizedUserAgent: runtime.openWebRxSession && runtime.openWebRxSession.lastAuthorizedUserAgent ? runtime.openWebRxSession.lastAuthorizedUserAgent : null,
lastEnsureSdrAtMs: runtime.openWebRxSession && Number.isFinite(runtime.openWebRxSession.lastEnsureSdrAtMs) lastEnsureSdrAtMs: runtime.openWebRxSession && Number.isFinite(runtime.openWebRxSession.lastEnsureSdrAtMs)
? runtime.openWebRxSession.lastEnsureSdrAtMs ? runtime.openWebRxSession.lastEnsureSdrAtMs
: 0 : 0
@@ -7010,6 +7061,8 @@ function clearOpenWebRxSession() {
runtime.openWebRxSession = { runtime.openWebRxSession = {
activeOwnerUserId: null, activeOwnerUserId: null,
expiresAtMs: 0, expiresAtMs: 0,
lastAuthorizedIp: null,
lastAuthorizedUserAgent: null,
lastEnsureSdrAtMs: 0 lastEnsureSdrAtMs: 0
}; };
runtime.openWebRxAntennaRoute = null; runtime.openWebRxAntennaRoute = null;