2026-05-11 22:43:42 -04:00
|
|
|
import { SignJWT, jwtVerify } from "jose";
|
2026-05-14 22:15:53 -04:00
|
|
|
import fs from "node:fs";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import crypto from "node:crypto";
|
|
|
|
|
import { DATA_DIR } from "@/lib/dataDir";
|
2026-05-11 22:43:42 -04:00
|
|
|
|
2026-05-14 22:15:53 -04:00
|
|
|
function loadJwtSecret() {
|
|
|
|
|
if (process.env.JWT_SECRET) return process.env.JWT_SECRET;
|
|
|
|
|
const file = path.join(DATA_DIR, "jwt-secret");
|
|
|
|
|
try {
|
|
|
|
|
return fs.readFileSync(file, "utf8").trim();
|
|
|
|
|
} catch {}
|
|
|
|
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
|
|
|
const generated = crypto.randomBytes(32).toString("hex");
|
|
|
|
|
fs.writeFileSync(file, generated, { mode: 0o600 });
|
|
|
|
|
return generated;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SECRET = new TextEncoder().encode(loadJwtSecret());
|
2026-05-11 22:43:42 -04:00
|
|
|
|
|
|
|
|
export function shouldUseSecureCookie(request) {
|
|
|
|
|
const forceSecureCookie = process.env.AUTH_COOKIE_SECURE === "true";
|
|
|
|
|
const forwardedProto = request?.headers?.get?.("x-forwarded-proto");
|
|
|
|
|
const isHttpsRequest = forwardedProto === "https";
|
|
|
|
|
return forceSecureCookie || isHttpsRequest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function createDashboardAuthToken(claims = {}) {
|
|
|
|
|
return new SignJWT({ authenticated: true, ...claims })
|
|
|
|
|
.setProtectedHeader({ alg: "HS256" })
|
|
|
|
|
.setIssuedAt()
|
|
|
|
|
.setExpirationTime("24h")
|
|
|
|
|
.sign(SECRET);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function verifyDashboardAuthToken(token) {
|
|
|
|
|
if (!token) return false;
|
|
|
|
|
try {
|
|
|
|
|
await jwtVerify(token, SECRET);
|
|
|
|
|
return true;
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getDashboardAuthSession(token) {
|
|
|
|
|
if (!token) return null;
|
|
|
|
|
try {
|
|
|
|
|
const { payload } = await jwtVerify(token, SECRET);
|
|
|
|
|
return payload;
|
|
|
|
|
} catch {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function setDashboardAuthCookie(cookieStore, request, claims = {}) {
|
|
|
|
|
const token = await createDashboardAuthToken(claims);
|
|
|
|
|
cookieStore.set("auth_token", token, {
|
|
|
|
|
httpOnly: true,
|
|
|
|
|
secure: shouldUseSecureCookie(request),
|
|
|
|
|
sameSite: "lax",
|
|
|
|
|
path: "/",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function clearDashboardAuthCookie(cookieStore) {
|
|
|
|
|
cookieStore.delete("auth_token");
|
|
|
|
|
}
|