import type { UserTotpStatus } from "~/utils/db/users.db.server"; import type { MfaStepUpCheckReason, MfaStepUpCheckResult } from "./step-up.server"; export { ADMIN_MFA_CHALLENGE_PATH, ADMIN_MFA_DEFAULT_REDIRECT_PATH, ADMIN_MFA_ENROLLMENT_PATH } from "./paths"; import { ADMIN_MFA_CHALLENGE_PATH, ADMIN_MFA_DEFAULT_REDIRECT_PATH } from "./paths"; export type AdminMfaEnforcementFailureReason = | "missing-user" | "missing-admin" | "missing-totp-status" | "totp-not-enabled" | "step-up-missing" | "step-up-malformed" | "step-up-wrong-user" | "step-up-stale"; export type AdminMfaEnforcementReason = "allowed" | AdminMfaEnforcementFailureReason; export type AdminMfaEnforcementResult = | { allowed: true; reason: "allowed"; redirectTo: string } | { allowed: false; reason: AdminMfaEnforcementFailureReason; redirectTo: string }; export type AdminMfaEnforcementInput = { requestUrl: string | URL; userId?: string | null; isAdmin?: boolean | null; totpStatus?: UserTotpStatus | null; stepUp: MfaStepUpCheckResult; redirectMode?: "host-aware"; appFallbackPath?: string; }; function isAdminMfaChallengePath(requestUrl: string | URL): boolean { try { const url = requestUrl instanceof URL ? requestUrl : new URL(requestUrl); return url.pathname === ADMIN_MFA_CHALLENGE_PATH; } catch { return false; } } function safePathFromRequestUrl(requestUrl: string | URL): string { try { const url = requestUrl instanceof URL ? requestUrl : new URL(requestUrl); return `${url.pathname}${url.search}${url.hash}`; } catch { return ADMIN_MFA_DEFAULT_REDIRECT_PATH; } } function challengeRedirectTo(requestUrl: string | URL): string { const redirectTo = safePathFromRequestUrl(requestUrl); const params = new URLSearchParams({ redirectTo }); return `${ADMIN_MFA_CHALLENGE_PATH}?${params.toString()}`; } function stepUpReason(reason: MfaStepUpCheckReason): AdminMfaEnforcementFailureReason { switch (reason) { case "missing": return "step-up-missing"; case "malformed": return "step-up-malformed"; case "wrong-user": return "step-up-wrong-user"; case "stale": return "step-up-stale"; } } export function enforceAdminMfa(input: AdminMfaEnforcementInput): AdminMfaEnforcementResult { if (!input.userId) { return { allowed: false, reason: "missing-user", redirectTo: "/login" }; } if (input.isAdmin !== true) { return { allowed: false, reason: "missing-admin", redirectTo: input.appFallbackPath ?? "/" }; } if (!input.totpStatus || !input.totpStatus.enabled) { return { allowed: true, reason: "allowed", redirectTo: safePathFromRequestUrl(input.requestUrl) }; } if (!input.stepUp.verified && !isAdminMfaChallengePath(input.requestUrl)) { return { allowed: false, reason: stepUpReason(input.stepUp.reason), redirectTo: challengeRedirectTo(input.requestUrl) }; } return { allowed: true, reason: "allowed", redirectTo: safePathFromRequestUrl(input.requestUrl) }; }