src/dashboard/routesAuth.ts

import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie"; import type { DashboardAppConfig, DashboardPublicHttpsEntrypoint } from "../dashboard.ts"; import { isPublicTunnelRequestHost } from "../services/publicIngressAccess.ts"; import type { DashboardApp } from "./shared.ts"; import { getRequestHost, readDashboardBody } from "./shared.ts";

const DASHBOARD_SESSION_COOKIE_NAME = "dashboard_session"; const DASHBOARD_SESSION_MAX_AGE_SECONDS = 60 * 60 * 12; type DashboardCookieContext = Parameters[0];

interface DashboardAuthState { authenticated: boolean; requiresToken: boolean; publicHttpsEnabled: boolean; authMethod: "none" | "open_local" | "header" | "session"; configurationError: string | null; }

interface AuthRouteDeps { appConfig: DashboardAppConfig; publicHttpsEntrypoint: DashboardPublicHttpsEntrypoint | null; }

export function isDashboardAuthSessionApiPath(apiPath: string) { return String(apiPath || "").trim() === "/auth/session"; }

export async function hasValidDashboardSessionCookie( c: DashboardCookieContext, dashboardToken: string ) { const normalizedToken = String(dashboardToken || "").trim(); if (!normalizedToken) return false; const cookieValue = await getSignedCookie(c, normalizedToken, DASHBOARD_SESSION_COOKIE_NAME); return typeof cookieValue === "string" && cookieValue.length > 0; }

export function attachAuthRoutes(app: DashboardApp, deps: AuthRouteDeps) { const { appConfig, publicHttpsEntrypoint } = deps;

app.get("/api/auth/session", async (c) => { c.header("Cache-Control", "no-store"); if (isRequestFromPublicTunnel(c, publicHttpsEntrypoint)) { return c.json({ error: "Not found." }, 404); } return c.json(await buildDashboardAuthState(c, deps)); });

app.post("/api/auth/session", async (c) => { c.header("Cache-Control", "no-store"); if (isRequestFromPublicTunnel(c, publicHttpsEntrypoint)) { return c.json({ error: "Not found." }, 404); }

const dashboardToken = String(appConfig.dashboardToken || "").trim();
const publicHttpsEnabled = Boolean(publicHttpsEntrypoint?.getState?.()?.enabled);
if (!dashboardToken) {
  if (publicHttpsEnabled) {
    return c.json(
      {
        error: "dashboard_token_required_when_public_https_enabled"
      },
      503
    );
  }
  return c.json(await buildDashboardAuthState(c, deps));
}

const body = await readDashboardBody(c);
const presentedToken = String(body.token || "").trim();
if (!presentedToken || presentedToken !== dashboardToken) {
  return c.json({ error: "Unauthorized. Provide a valid dashboard token." }, 401);
}

await setSignedCookie(c, DASHBOARD_SESSION_COOKIE_NAME, crypto.randomUUID(), dashboardToken, {
  httpOnly: true,
  maxAge: DASHBOARD_SESSION_MAX_AGE_SECONDS,
  path: "/",
  sameSite: "Strict",
  secure: isSecureDashboardRequest(c)
});

return c.json({
  authenticated: true,
  requiresToken: true,
  publicHttpsEnabled,
  authMethod: "session",
  configurationError: null
} satisfies DashboardAuthState);

});

app.delete("/api/auth/session", async (c) => { c.header("Cache-Control", "no-store"); if (isRequestFromPublicTunnel(c, publicHttpsEntrypoint)) { return c.json({ error: "Not found." }, 404); }

deleteCookie(c, DASHBOARD_SESSION_COOKIE_NAME, {
  path: "/"
});

const dashboardToken = String(appConfig.dashboardToken || "").trim();
const publicHttpsEnabled = Boolean(publicHttpsEntrypoint?.getState?.()?.enabled);
if (!dashboardToken) {
  return c.json({
    authenticated: !publicHttpsEnabled,
    requiresToken: publicHttpsEnabled,
    publicHttpsEnabled,
    authMethod: publicHttpsEnabled ? "none" : "open_local",
    configurationError: publicHttpsEnabled ? "dashboard_token_required_when_public_https_enabled" : null
  } satisfies DashboardAuthState);
}

return c.json({
  authenticated: false,
  requiresToken: true,
  publicHttpsEnabled,
  authMethod: "none",
  configurationError: null
} satisfies DashboardAuthState);

}); }

async function buildDashboardAuthState( c: DashboardCookieContext, { appConfig, publicHttpsEntrypoint }: AuthRouteDeps ): Promise { const dashboardToken = String(appConfig.dashboardToken || "").trim(); const publicHttpsEnabled = Boolean(publicHttpsEntrypoint?.getState?.()?.enabled);

if (!dashboardToken) { if (publicHttpsEnabled) { return { authenticated: false, requiresToken: true, publicHttpsEnabled, authMethod: "none", configurationError: "dashboard_token_required_when_public_https_enabled" }; }

return {
  authenticated: true,
  requiresToken: false,
  publicHttpsEnabled,
  authMethod: "open_local",
  configurationError: null
};

}

const presentedDashboardToken = String(c.req.header("x-dashboard-token") || "").trim(); if (presentedDashboardToken && presentedDashboardToken === dashboardToken) { return { authenticated: true, requiresToken: true, publicHttpsEnabled, authMethod: "header", configurationError: null }; }

const hasSessionCookie = await hasValidDashboardSessionCookie(c, dashboardToken); return { authenticated: hasSessionCookie, requiresToken: true, publicHttpsEnabled, authMethod: hasSessionCookie ? "session" : "none", configurationError: null }; }

function isSecureDashboardRequest( c: DashboardCookieContext ) { const forwardedProto = String(c.req.header("x-forwarded-proto") || "").trim().toLowerCase(); if (forwardedProto === "https") return true; try { return new URL(c.req.url).protocol === "https:"; } catch { return false; } }

function isRequestFromPublicTunnel( c: DashboardCookieContext, publicHttpsEntrypoint: DashboardPublicHttpsEntrypoint | null | undefined ) { const requestHost = getRequestHost(c); if (!requestHost) return false; const publicState = publicHttpsEntrypoint?.getState?.() || null; return isPublicTunnelRequestHost(requestHost, publicState); }