55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
/**
|
|
* Phase 09 Task #82 — Thin Audit-Event wrapper for admin-sso paths.
|
|
*
|
|
* Centralises action names + metadata sanitization for the login / logout
|
|
* / fallback / mfa-challenge paths. Sits on top of
|
|
* `@toolctrl/db/audit-events.server` which already handles redaction.
|
|
*/
|
|
import { createAuditEvent } from "@toolctrl/db/audit-events.server";
|
|
import type { Prisma } from "@toolctrl/db/prisma-browser";
|
|
|
|
export type AdminSsoAuditAction =
|
|
| "admin.login.sso.success"
|
|
| "admin.login.sso.failure"
|
|
| "admin.login.fallback.success"
|
|
| "admin.login.fallback.failure"
|
|
| "admin.logout.sso"
|
|
| "admin.mfa.app.challenge.success"
|
|
| "admin.mfa.app.challenge.failure";
|
|
|
|
export type AdminSsoAuditInput = {
|
|
readonly action: AdminSsoAuditAction;
|
|
readonly actorAdminUserId?: string | null;
|
|
readonly ssoSubjectId?: string | null;
|
|
readonly request?: Request;
|
|
readonly meta?: Record<string, unknown>;
|
|
};
|
|
|
|
function clientMeta(request: Request | undefined): { ip: string | null; userAgent: string | null } {
|
|
if (!request) return { ip: null, userAgent: null };
|
|
const fwd = request.headers.get("x-forwarded-for") ?? "";
|
|
const ip = fwd.split(",")[0]?.trim() || request.headers.get("x-real-ip") || null;
|
|
const userAgent = request.headers.get("user-agent") || null;
|
|
return { ip, userAgent };
|
|
}
|
|
|
|
export async function writeAdminSsoAuditEvent(input: AdminSsoAuditInput): Promise<void> {
|
|
const { ip, userAgent } = clientMeta(input.request);
|
|
try {
|
|
await createAuditEvent({
|
|
action: input.action,
|
|
actorType: "admin",
|
|
actorId: input.actorAdminUserId ?? null,
|
|
metadata: {
|
|
ssoSubjectId: input.ssoSubjectId ?? null,
|
|
ip,
|
|
userAgent,
|
|
...input.meta,
|
|
} as unknown as Prisma.InputJsonValue,
|
|
});
|
|
} catch {
|
|
// Audit failures must never block the auth flow. The DB-level error
|
|
// already lands in Sentry via the audit-events boundary.
|
|
}
|
|
}
|