feat(phase1c): audit sign-in / sign-out via better-auth after hook

This commit is contained in:
Semih
2026-05-13 10:40:26 +00:00
parent f72dcc05ba
commit e4e734fc66

View File

@@ -1,7 +1,10 @@
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { createAuthMiddleware } from "better-auth/api";
import { prisma } from "./db";
const AUDITED_PATHS = new Set(["/sign-in/email", "/sign-out"]);
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: "postgresql" }),
secret: process.env.BETTER_AUTH_SECRET,
@@ -21,4 +24,34 @@ export const auth = betterAuth({
cookiePrefix: "sp",
useSecureCookies: process.env.NODE_ENV === "production",
},
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (!AUDITED_PATHS.has(ctx.path)) return;
const status = ctx.context.returned ? 200 : 400;
const userId =
(ctx.context.returned as { user?: { id?: string } } | undefined)?.user?.id ??
ctx.context.session?.user.id ??
null;
const ip =
ctx.request?.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ??
ctx.request?.headers.get("x-real-ip") ??
null;
const ua = ctx.request?.headers.get("user-agent") ?? null;
try {
await prisma.auditLog.create({
data: {
actorUserId: userId,
projectKey: "panel",
endpoint: ctx.path,
method: ctx.method ?? "POST",
responseStatus: status,
sourceIp: ip,
userAgent: ua,
},
});
} catch {
// never break auth on audit failure
}
}),
},
});