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 { WebSocketServer, WebSocket } = require("ws");
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
const WEB_PORT = Number(process.env.AFK_PARENT_PORT || 3008);
|
||||
const PARENT_SECRET = process.env.PARENT_SECRET || "";
|
||||
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 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@ -63,6 +68,40 @@ function listCachedBots() {
|
||||
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 ─────────────────────────────────
|
||||
{
|
||||
const _log = console.log.bind(console);
|
||||
@ -107,6 +146,11 @@ const httpServer = http.createServer((req, res) => {
|
||||
}
|
||||
|
||||
if (req.url === "/" || req.url === "/index.html") {
|
||||
if (!isUiAuthorized(req)) {
|
||||
requestUiAuth(res);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const html = fs.readFileSync(WEB_UI_PATH, "utf8");
|
||||
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),
|
||||
);
|
||||
} 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.emit("connection", ws),
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user