mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
implement CLI token validation for enhanced security
This commit is contained in:
@@ -1,11 +1,27 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { jwtVerify } from "jose";
|
import { jwtVerify } from "jose";
|
||||||
import { getSettings } from "@/lib/localDb";
|
import { getSettings } from "@/lib/localDb";
|
||||||
|
import { getConsistentMachineId } from "@/shared/utils/machineId";
|
||||||
|
|
||||||
const SECRET = new TextEncoder().encode(
|
const SECRET = new TextEncoder().encode(
|
||||||
process.env.JWT_SECRET || "9router-default-secret-change-me"
|
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
|
// Always require JWT token regardless of requireLogin setting
|
||||||
const ALWAYS_PROTECTED = [
|
const ALWAYS_PROTECTED = [
|
||||||
"/api/shutdown",
|
"/api/shutdown",
|
||||||
@@ -20,12 +36,6 @@ const PROTECTED_API_PATHS = [
|
|||||||
"/api/provider-nodes/validate",
|
"/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) {
|
async function hasValidToken(request) {
|
||||||
const token = request.cookies.get("auth_token")?.value;
|
const token = request.cookies.get("auth_token")?.value;
|
||||||
if (!token) return false;
|
if (!token) return false;
|
||||||
@@ -55,19 +65,18 @@ async function isAuthenticated(request) {
|
|||||||
|
|
||||||
export async function proxy(request) {
|
export async function proxy(request) {
|
||||||
const { pathname } = request.nextUrl;
|
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 (ALWAYS_PROTECTED.some((p) => pathname.startsWith(p))) {
|
||||||
if (isLocal || await hasValidToken(request))
|
if (await hasValidCliToken(request) || await hasValidToken(request))
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
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 (PROTECTED_API_PATHS.some((p) => pathname.startsWith(p))) {
|
||||||
if (pathname === "/api/settings/require-login") return NextResponse.next();
|
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.next();
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user