feat: implement basic authentication for UI access
This commit is contained in:
parent
49b89b3d81
commit
4a4cafecf1
52
server.js
52
server.js
@ -22,9 +22,14 @@ const fs = require("fs");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { WebSocketServer, WebSocket } = require("ws");
|
const { WebSocketServer, WebSocket } = require("ws");
|
||||||
|
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
const WEB_PORT = Number(process.env.AFK_PARENT_PORT || 3008);
|
const WEB_PORT = Number(process.env.AFK_PARENT_PORT || 3008);
|
||||||
const PARENT_SECRET = process.env.PARENT_SECRET || "";
|
const PARENT_SECRET = process.env.PARENT_SECRET || "";
|
||||||
const WEB_UI_PATH = path.join(__dirname, "web", "index.html");
|
const WEB_UI_PATH = path.join(__dirname, "web", "index.html");
|
||||||
|
const UI_AUTH_USER = process.env.UI_AUTH_USER || "admin";
|
||||||
|
const UI_AUTH_PASS = process.env.UI_AUTH_PASS || "afk";
|
||||||
|
const UI_AUTH_REALM = process.env.UI_AUTH_REALM || "AFK Bot UI";
|
||||||
|
|
||||||
// ─── state ───────────────────────────────────────────────────────────────────
|
// ─── state ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -63,6 +68,40 @@ function listCachedBots() {
|
|||||||
return [...cachedBots.values()];
|
return [...cachedBots.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeBasicAuth(authHeader) {
|
||||||
|
if (!authHeader || !authHeader.startsWith("Basic ")) return null;
|
||||||
|
const base64 = authHeader.slice("Basic ".length).trim();
|
||||||
|
if (!base64) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = Buffer.from(base64, "base64").toString("utf8");
|
||||||
|
const sep = decoded.indexOf(":");
|
||||||
|
if (sep === -1) return null;
|
||||||
|
return {
|
||||||
|
user: decoded.slice(0, sep),
|
||||||
|
pass: decoded.slice(sep + 1),
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUiAuthorized(req) {
|
||||||
|
const auth = decodeBasicAuth(req.headers.authorization || "");
|
||||||
|
return Boolean(
|
||||||
|
auth && auth.user === UI_AUTH_USER && auth.pass === UI_AUTH_PASS,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestUiAuth(res) {
|
||||||
|
res.writeHead(401, {
|
||||||
|
"Content-Type": "text/plain; charset=utf-8",
|
||||||
|
"WWW-Authenticate": `Basic realm="${UI_AUTH_REALM}"`,
|
||||||
|
"Cache-Control": "no-store",
|
||||||
|
});
|
||||||
|
res.end("Authentication required");
|
||||||
|
}
|
||||||
|
|
||||||
// ─── forward server console logs to browsers ─────────────────────────────────
|
// ─── forward server console logs to browsers ─────────────────────────────────
|
||||||
{
|
{
|
||||||
const _log = console.log.bind(console);
|
const _log = console.log.bind(console);
|
||||||
@ -107,6 +146,11 @@ const httpServer = http.createServer((req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.url === "/" || req.url === "/index.html") {
|
if (req.url === "/" || req.url === "/index.html") {
|
||||||
|
if (!isUiAuthorized(req)) {
|
||||||
|
requestUiAuth(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const html = fs.readFileSync(WEB_UI_PATH, "utf8");
|
const html = fs.readFileSync(WEB_UI_PATH, "utf8");
|
||||||
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
||||||
@ -167,6 +211,14 @@ httpServer.on("upgrade", (req, socket, head) => {
|
|||||||
parentWssI.emit("connection", ws, req),
|
parentWssI.emit("connection", ws, req),
|
||||||
);
|
);
|
||||||
} else if (pathname === "/ws") {
|
} else if (pathname === "/ws") {
|
||||||
|
if (!isUiAuthorized(req)) {
|
||||||
|
socket.write(
|
||||||
|
`HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm="${UI_AUTH_REALM}"\r\nConnection: close\r\n\r\n`,
|
||||||
|
);
|
||||||
|
socket.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
browserWss.handleUpgrade(req, socket, head, (ws) =>
|
browserWss.handleUpgrade(req, socket, head, (ws) =>
|
||||||
browserWss.emit("connection", ws),
|
browserWss.emit("connection", ws),
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user