import { useState, useEffect } from 'react'; import { useAuth } from './useAuth'; import { useRequireMFA } from './useRequireMFA'; import { getSessionAal } from '@/lib/authService'; import { logger } from '@/lib/logger'; import { handleNonCriticalError } from '@/lib/errorHandler'; /** * Phase 3: Session Monitoring Hook * Monitors AAL degradation and forces re-verification when needed * * This hook continuously checks the session's AAL level and detects * if it degrades from AAL2 to AAL1, which can happen after token refresh * or session expiry. */ export function useSessionMonitor() { const { aal, session, user } = useAuth(); const { requiresMFA, isEnrolled } = useRequireMFA(); const [aalWarning, setAalWarning] = useState(false); const [aalDegraded, setAalDegraded] = useState(false); useEffect(() => { if (!session || !user || !requiresMFA || !isEnrolled) { setAalWarning(false); setAalDegraded(false); return; } // Check AAL every 60 seconds const interval = setInterval(async () => { try { const currentAal = await getSessionAal(session); // If AAL degraded from AAL2 to AAL1 if (currentAal === 'aal1' && aal === 'aal2') { logger.warn('AAL degradation detected', { userId: user.id, previousAal: aal, currentAal, action: 'session_monitor' }); // Show warning for 30 seconds setAalWarning(true); setAalDegraded(true); // After 30 seconds, redirect to MFA step-up setTimeout(() => { logger.info('Forcing MFA step-up due to AAL degradation', { userId: user.id, action: 'session_monitor_redirect' }); sessionStorage.setItem('mfa_step_up_required', 'true'); sessionStorage.setItem('mfa_intended_path', window.location.pathname); window.location.href = '/auth'; }, 30000); } } catch (error: unknown) { handleNonCriticalError(error, { action: 'Session monitor check', userId: user.id, }); } }, 60000); return () => clearInterval(interval); }, [session, aal, requiresMFA, isEnrolled, user]); return { aalWarning, aalDegraded }; }