Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/utils/auth/mfa/enforcement.server.ts
T

88 lines
2.9 KiB
TypeScript

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) };
}