"use client"; import Link from "next/link"; import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; import { trpc } from "~/lib/trpc/client.js"; const SNOOZE_KEY = "capakraken_mfa_prompt_snoozed_until"; const SNOOZE_DAYS = 7; /** * Banner shown to ADMIN / MANAGER users who have not yet enabled TOTP MFA. * Fetches MFA status client-side via tRPC so the banner reacts immediately * after the user enables MFA — no full-page reload required. * Snooze state is scoped by userId to prevent cross-user leakage on shared browsers. */ export function MfaPromptBanner() { const { data: mfaStatus } = trpc.user.getMfaStatus.useQuery(); const { data: session } = useSession(); const userId = (session?.user as { id?: string } | undefined)?.id ?? ""; const [snoozed, setSnoozed] = useState(null); // Read snooze state from localStorage on mount (keyed by userId) useEffect(() => { if (!userId) return; try { const raw = localStorage.getItem(`${SNOOZE_KEY}_${userId}`); if (raw) { const until = Number(raw); if (!isNaN(until) && Date.now() < until) { setSnoozed(true); return; } } } catch { // localStorage unavailable } setSnoozed(false); }, [userId]); function snooze() { try { const until = Date.now() + SNOOZE_DAYS * 24 * 60 * 60 * 1000; localStorage.setItem(`${SNOOZE_KEY}_${userId}`, String(until)); } catch { // ignore } setSnoozed(true); } // Don't render until we know the MFA status and snooze state if (mfaStatus === undefined || snoozed === null) return null; // Already enabled — no banner needed if (mfaStatus.totpEnabled) return null; // Snoozed if (snoozed) return null; return (
Protect your account:{" "} Your role has elevated permissions. We recommend enabling multi-factor authentication (MFA).
Set up MFA
); }