auth-yes/src/main.ts

135 lines
3.9 KiB
TypeScript

import { Hono } from "jsr:@hono/hono@4";
import { serveStatic } from "jsr:@hono/hono@4/deno";
import { deleteCookie } from "jsr:@hono/hono@4/cookie";
import { MetadataService } from "jsr:@simplewebauthn/server@13";
import { initDb, sqlWrapper } from "./core/db.ts";
import { pingValkey, valkey } from "./core/valkey.ts";
import { contentNegotiation } from "./core/content_negotiation.ts";
import { payloadCapGuard } from "./core/auth_guards.ts";
import { startConnectRpcServer } from "./core/rpc.ts";
import {
extractAllSessionIds,
getAuthenticatedUser,
getCookieDomain,
} from "./core/session.ts";
import { isSafeRedirectUrl } from "./core/forward_auth.ts";
import { auditWrapper } from "./core/audit.ts";
import { authRoutes } from "./features/auth/routes.tsx";
import { adminRoutes } from "./features/admin/routes.tsx";
import { eventsRoutes } from "./features/events/routes.tsx";
import { sessionRoutes } from "./features/sessions/routes.tsx";
import { passRoutes } from "./features/sessions/pass_routes.ts";
import { forwardAuthRoutes } from "./features/forward_auth/routes.ts";
const app: Hono = new Hono();
app.use("*", payloadCapGuard);
app.use("*", contentNegotiation());
// Serve static assets (specifically Datastar, WebAuthn scripts, and stylesheets)
app.use("/public/*", serveStatic({ root: "./" }));
// Root, Navigation & Logout Endpoints
app.get("/", (c) => {
c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
return c.redirect("/login");
});
app.get("/logout", async (c) => {
const sessionIds = extractAllSessionIds(c);
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 (sessionIds.length > 0) {
try {
const authUser = await getAuthenticatedUser(c);
if (authUser) {
userId = authUser.userId;
}
for (const sId of sessionIds) {
try {
await valkey.del(sId);
await sqlWrapper.sql`DELETE FROM sessions WHERE id = ${sId}`;
} catch (_e) {}
}
} catch (_e) {}
}
if (rawRedirect && isSafeRedirectUrl(rawRedirect)) {
safeRedirect = rawRedirect;
}
auditWrapper.auditLog(userId, "logout_success", "session", null, userIp);
const cookieDomain = getCookieDomain();
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",
});
c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
return c.redirect(safeRedirect || "/login");
});
// Wire Sub-routers and Vertical Slices
app.route("/pass", passRoutes);
app.route("/", forwardAuthRoutes);
app.route("/", authRoutes);
app.route("/", eventsRoutes);
app.route("/", sessionRoutes);
app.route("/admin", adminRoutes);
app.route("/api/admin", adminRoutes);
// Workload Mesh ConnectRPC Daemon
startConnectRpcServer(app);
// Basic health check
app.get("/healthz", (c) => c.text("OK"));
if (import.meta.main) {
const rpID = Deno.env.get("RP_ID");
const origin = Deno.env.get("ORIGIN");
if (!rpID || !origin) {
console.warn(
"[Auth-Yes] Warning: RP_ID or ORIGIN not set in environment. Falling back to defaults.",
);
}
console.log("[Auth-Yes] Initializing FIDO MDS3 Metadata Blob...");
try {
await MetadataService.initialize();
console.log("[Auth-Yes] FIDO MDS3 Metadata Blob successfully loaded.");
} catch (error) {
console.warn(
"[Auth-Yes] Failed to initialize FIDO MDS3 Metadata Blob (offline mode):",
error,
);
}
console.log("[Auth-Yes] Initializing database and cache...");
await initDb();
await pingValkey();
const port = parseInt(Deno.env.get("PORT") || "8000", 10);
console.log(`[Auth-Yes] Hypermedia Server running on port ${port}`);
Deno.serve({ port }, app.fetch);
}
export default app;