import { Hono } from "jsr:@hono/hono@4"; import { serveStatic } from "jsr:@hono/hono@4/deno"; import { deleteCookie, getCookie } from "jsr:@hono/hono@4/cookie"; import { sql } from "../server/db.ts"; import { valkey } from "../server/valkey.ts"; import { getAuthenticatedUser, getCookieDomain, isGlobalAdmin, isSafeRedirectUrl, } from "../server/auth-session.ts"; import { auditWrapper } from "../server/audit.ts"; import { LoginPage } from "./components/LoginPage.tsx"; import { RegisterPage } from "./components/RegisterPage.tsx"; import { SessionsPage } from "./components/SessionsPage.tsx"; import { PasskeysPage } from "./components/PasskeysPage.tsx"; import { AuditLogPage } from "./components/AuditLogPage.tsx"; import { AdminUsersPage } from "./components/AdminUsersPage.tsx"; import { AdminUserDetailsPage } from "./components/AdminUserDetailsPage.tsx"; import { AAGUIDPage } from "./components/AAGUIDPage.tsx"; import { RecoveryPage } from "./components/RecoveryPage.tsx"; import { AdminAppsPage } from "./components/AdminAppsPage.tsx"; import { AdminRolesPage } from "./components/AdminRolesPage.tsx"; import { AdminInvitesPage } from "./components/AdminInvitesPage.tsx"; import { AppLaunchpadPage } from "./components/AppLaunchpadPage.tsx"; const uiApp: Hono = new Hono(); // Explicit Side Effect: Route rendering uiApp.get("/", (c) => { return c.redirect("/login"); }); uiApp.get("/logout", async (c) => { const sessionId = getCookie(c, "session_id"); const rawRedirect = c.req.query("redirect"); let safeRedirect = null; const userIp = c.req.header("x-forwarded-for") || "127.0.0.1"; let userId = null; if (sessionId) { try { // Get user ID for auditing before we delete the session const authUser = await getAuthenticatedUser(c); if (authUser) { userId = authUser.userId; } await valkey.del(sessionId); await sql`DELETE FROM sessions WHERE id = ${sessionId}`; } catch (_e) { // Best effort cleanup } } if (rawRedirect) { if (isSafeRedirectUrl(rawRedirect)) { safeRedirect = rawRedirect; } else { auditWrapper.auditLog( userId, "open_redirect_intercepted", "logout_redirect", { raw_url: rawRedirect }, userIp, ); } } auditWrapper.auditLog( userId, "logout_success", "session", null, userIp, ); const rpID = Deno.env.get("RP_ID") || ""; const cookieDomain = getCookieDomain(rpID); if (cookieDomain) { deleteCookie(c, "session_id", { domain: cookieDomain, path: "/", httpOnly: true, secure: true, sameSite: "Lax", }); } deleteCookie(c, "session_id", { path: "/", httpOnly: true, secure: true, sameSite: "Lax", }); if (safeRedirect) { return c.redirect(`/login?redirect=${encodeURIComponent(safeRedirect)}`); } return c.redirect("/login"); }); uiApp.get("/login", (c) => { return c.html(LoginPage()); }); uiApp.get("/recovery", (c) => { return c.html(RecoveryPage()); }); uiApp.get("/register", (c) => { const initialCode = c.req.query("code") || ""; return c.html(RegisterPage({ initialCode })); }); uiApp.get("/dashboard", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); let apps = []; if (isAdmin) { apps = await sql` SELECT id, name, description, domain, 'Admin' as role FROM apps WHERE domain IS NOT NULL ORDER BY name ASC ` as any[]; } else { apps = await sql` SELECT a.id, a.name, a.description, a.domain, g.role FROM apps a JOIN grants g ON a.id = g.app_id WHERE g.user_id = ${auth.userId} AND a.domain IS NOT NULL ORDER BY a.name ASC ` as any[]; } return c.html(AppLaunchpadPage({ apps: apps as any, isAdmin })); }); uiApp.get("/dashboard/sessions", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); const sessions = await sql` SELECT id, created_at, expires_at FROM sessions WHERE user_id = ${auth.userId} AND expires_at > NOW() ORDER BY created_at DESC `; return c.html( SessionsPage({ sessions, currentSessionId: auth.sessionId, isAdmin }), ); }); uiApp.get("/dashboard/passkeys", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); const passkeys = await sql` SELECT id, credential_id, counter FROM passkeys WHERE user_id = ${auth.userId} `; return c.html(PasskeysPage({ passkeys, isAdmin })); }); // Admin Routes uiApp.get("/admin", (c) => { return c.redirect("/admin/users"); }); uiApp.get("/admin/users", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const users = await sql` SELECT id, username, display_name, account_status FROM users ORDER BY username ASC `; return c.html(AdminUsersPage({ users })); }); uiApp.get("/admin/apps", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const apps = await sql` SELECT a.id, a.name, a.spiffe_id, a.description, a.created_at, COUNT(g.id) AS active_grants_count FROM apps a LEFT JOIN grants g ON a.id = g.app_id GROUP BY a.id, a.name, a.spiffe_id, a.description, a.created_at ORDER BY a.created_at ASC `; return c.html(AdminAppsPage({ apps })); }); uiApp.get("/admin/roles", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const roles = await sql` SELECT r.id, r.name, r.description, r.app_id, r.created_at, a.name AS app_name FROM roles r LEFT JOIN apps a ON r.app_id = a.id ORDER BY r.app_id NULLS FIRST, r.name ASC `; const apps = await sql` SELECT id, name, spiffe_id FROM apps ORDER BY name ASC `; return c.html(AdminRolesPage({ roles, apps })); }); uiApp.get("/admin/invites", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const invites = await sql` SELECT i.id, i.code, i.role, i.max_uses, i.uses_count, i.auto_activate, i.expires_at, i.created_at, i.used_at, a.name AS app_name, a.id AS app_id, u.username AS used_by_username FROM invites i LEFT JOIN apps a ON i.app_id = a.id LEFT JOIN users u ON i.used_by = u.id ORDER BY i.created_at DESC `; const apps = await sql` SELECT id, name, spiffe_id FROM apps ORDER BY name ASC `; const allRoles = await sql` SELECT id, name, description, app_id FROM roles ORDER BY name ASC `; return c.html(AdminInvitesPage({ invites, apps, allRoles })); }); uiApp.get("/admin/aaguid", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const allowlist = await sql` SELECT id, aaguid, description, created_at FROM aaguid_allowlist ORDER BY created_at DESC `; return c.html(AAGUIDPage({ allowlist })); }); uiApp.get("/admin/users/:id", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/admin/users"); } const targetUserId = c.req.param("id"); const user = await sql` SELECT id, username, display_name, account_status FROM users WHERE id = ${targetUserId} `.then((res) => res[0]); if (!user) { return c.redirect("/admin/users"); } const sessions = await sql` SELECT id, created_at, expires_at FROM sessions WHERE user_id = ${targetUserId} AND expires_at > NOW() ORDER BY created_at DESC `; const passkeys = await sql` SELECT id, credential_id, counter FROM passkeys WHERE user_id = ${targetUserId} `; const grants = await sql` SELECT g.id, g.app_id, g.role, g.created_at, a.name AS app_name, a.spiffe_id FROM grants g JOIN apps a ON g.app_id = a.id WHERE g.user_id = ${targetUserId} ORDER BY a.name ASC `; const allApps = await sql` SELECT id, name, spiffe_id FROM apps ORDER BY name ASC `; const allRoles = await sql` SELECT id, name, description, app_id FROM roles ORDER BY name ASC `; return c.html( AdminUserDetailsPage({ user, sessions, passkeys, grants, allApps, allRoles, }), ); }); uiApp.get("/admin/audit-logs", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { return c.redirect("/login"); } const isAdmin = await isGlobalAdmin(auth.userId); if (!isAdmin) { return c.redirect("/dashboard"); } const logs = await sql` SELECT a.id, a.action, a.resource, a.details, a.ip_address, a.created_at, u.username as user FROM audit_records a LEFT JOIN users u ON a.user_id = u.id ORDER BY a.created_at DESC LIMIT 100 `; return c.html(AuditLogPage({ logs })); }); // Explicit Side Effect: Serving static assets (client-side JS) with no-cache headers to prevent stale browser caches uiApp.use("/public/*", async (c, next) => { await next(); c.header("Cache-Control", "no-cache, no-store, must-revalidate"); }); uiApp.get( "/public/*", serveStatic({ root: "./ui", rewriteRequestPath: (path) => path.replace(/^\/public/, "/public"), }), ); export { uiApp };