From 428e2c045cb9c0eb8080e8b580471a9c2eaa95ca Mon Sep 17 00:00:00 2001 From: decolua Date: Thu, 23 Apr 2026 17:43:35 +0700 Subject: [PATCH] implement CLI token validation for enhanced security --- src/dashboardGuard.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dashboardGuard.js b/src/dashboardGuard.js index 6d4f5277..12c1d305 100644 --- a/src/dashboardGuard.js +++ b/src/dashboardGuard.js @@ -1,11 +1,27 @@ import { NextResponse } from "next/server"; import { jwtVerify } from "jose"; import { getSettings } from "@/lib/localDb"; +import { getConsistentMachineId } from "@/shared/utils/machineId"; const SECRET = new TextEncoder().encode( process.env.JWT_SECRET || "9router-default-secret-change-me" ); +const CLI_TOKEN_HEADER = "x-9r-cli-token"; +const CLI_TOKEN_SALT = "9r-cli-auth"; + +let cachedCliToken = null; +async function getCliToken() { + if (!cachedCliToken) cachedCliToken = await getConsistentMachineId(CLI_TOKEN_SALT); + return cachedCliToken; +} + +async function hasValidCliToken(request) { + const token = request.headers.get(CLI_TOKEN_HEADER); + if (!token) return false; + return token === await getCliToken(); +} + // Always require JWT token regardless of requireLogin setting const ALWAYS_PROTECTED = [ "/api/shutdown", @@ -20,12 +36,6 @@ const PROTECTED_API_PATHS = [ "/api/provider-nodes/validate", ]; -function isLocalRequest(request) { - const host = request.headers.get("host") || ""; - const hostname = host.split(":")[0]; - return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1"; -} - async function hasValidToken(request) { const token = request.cookies.get("auth_token")?.value; if (!token) return false; @@ -55,19 +65,18 @@ async function isAuthenticated(request) { export async function proxy(request) { const { pathname } = request.nextUrl; - const isLocal = isLocalRequest(request); - // Always protected - allow localhost or valid JWT only + // Always protected - require valid JWT or local CLI token (machineId-based) if (ALWAYS_PROTECTED.some((p) => pathname.startsWith(p))) { - if (isLocal || await hasValidToken(request)) + if (await hasValidCliToken(request) || await hasValidToken(request)) return NextResponse.next(); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } - // Protect sensitive API endpoints (bypass if localhost or requireLogin = false) + // Protect sensitive API endpoints (allow CLI token, JWT, or requireLogin=false) if (PROTECTED_API_PATHS.some((p) => pathname.startsWith(p))) { if (pathname === "/api/settings/require-login") return NextResponse.next(); - if (isLocal || await isAuthenticated(request)) + if (await hasValidCliToken(request) || await isAuthenticated(request)) return NextResponse.next(); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); }