import { useState, useEffect, useRef, useCallback } from 'react'; import { req, getToken, saveToken, ENDPOINTS } from '../api'; import { Card, CardHeader, Callout, FormGroup, ResponseBox } from '../components/ui'; const { AUTH, INTEL } = ENDPOINTS; // ── Trust Gauge ─────────────────────────────────────────────────────────────── const TRUST_ARC = 204; function TrustGauge({ score, label, color }) { const offset = TRUST_ARC - (score / 100) * TRUST_ARC; return (
{Math.round(score)} / 100
{(label || 'loading').toUpperCase()}
); } // ── Behavior bar ────────────────────────────────────────────────────────────── function BehaviorBar({ label, value }) { const color = value > 0.7 ? '#16a34a' : value > 0.4 ? '#d97706' : '#dc2626'; return (
{label} {value.toFixed(2)}
); } // ── Travel result renderer ──────────────────────────────────────────────────── function TravelResult({ data }) { if (!data) return null; const CMAP = { impossible:'#dc2626', suspicious:'#f97316', plausible:'#16a34a', same_area:'#16a34a', coords_unknown:'#94a3b8' }; const IMAP = { impossible:'🚨', suspicious:'⚠️', plausible:'✅', same_area:'✅', coords_unknown:'❓' }; const col = CMAP[data.verdict] || '#94a3b8'; const icon = IMAP[data.verdict] || '❓'; return (
{icon} {(data.verdict || '').toUpperCase().replace('_', ' ')}
{data.message}
{[ { v: data.distance_km || 0, l: 'Distance', u: 'km' }, { v: Math.round(data.speed_kmh || 0), l: 'Speed', u: 'km/h' }, { v: Math.round(data.time_gap_minutes || 0), l: 'Gap', u: 'min' }, ].map(s => (
{s.v} {s.u}
{s.l}
))}
{data.trust_delta < 0 && (
⚠ Trust impact: {data.trust_delta} pts
)}
); } // ── AI Anomaly result ───────────────────────────────────────────────────────── function AnomalyResult({ data }) { if (!data) return null; return (
{data.anomaly_score.toFixed(1)} / 100
{data.classification}
Confidence: {(data.confidence * 100).toFixed(0)}% · Statistical Isolation Forest
{Object.entries(data.per_feature || {}).map(([fn, fs]) => { const fc = fs > 60 ? '#dc2626' : fs > 30 ? '#d97706' : '#16a34a'; return (
{fn}{fs.toFixed(1)}
); })}
); } export default function IntelTab({ onTokenSave }) { // Login status const [loginStatus, setLoginStatus] = useState(''); // Trust const [trust, setTrust] = useState({ score: 0, label: 'loading', color: '#94a3b8' }); const [trustHistory, setTrustHist] = useState([]); const [trustResp, setTrustResp] = useState(null); // Behavior const [collecting, setCollecting] = useState(false); const [bhScores, setBhScores] = useState({ te: 0.5, ml: 0.6, sv: 0.5 }); const [collectStatus,setCollStatus] = useState(''); const [behaviorResp, setBehavResp] = useState(null); // Travel const [cities, setCities] = useState([]); const [travelFrom, setTravelFrom] = useState('New York'); const [travelTo, setTravelTo] = useState('Moscow'); const [travelHours, setTravelHours] = useState('2'); const [travelResult, setTravelResult]= useState(null); // AI Anomaly const [aiTyping, setAiTyping] = useState('0.70'); const [aiMouse, setAiMouse] = useState('0.62'); const [aiScroll, setAiScroll] = useState('0.48'); const [aiHour, setAiHour] = useState('0.55'); const [aiFailed, setAiFailed] = useState('0.00'); const [anomResult, setAnomResult]= useState(null); // Challenge const [showChallenge, setShowChallenge] = useState(false); const [challengeQ, setChallengeQ] = useState(''); const [challengeAnswer, setChallengeAnswer] = useState(''); const [challengeMsg, setChallengeMsg] = useState(''); const [challengeId, setChallengeId] = useState(null); const [challengeResp, setChallengeResp] = useState(null); // Explain const [expLoc, setExpLoc] = useState('85'); const [expDev, setExpDev] = useState('15'); const [expTime, setExpTime] = useState('10'); const [expVel, setExpVel] = useState('5'); const [expBeh, setExpBeh] = useState('20'); const [expLevel, setExpLevel] = useState('2'); const [explainResult, setExplainResult] = useState(null); // Session audit const [sessionResp, setSessionResp] = useState(null); // Loading const [loading, setLoading] = useState({}); const setLoad = (k, v) => setLoading(p => ({ ...p, [k]: v })); // ── Behavior refs ───────────────────────────────────────────────────────── const bhKeyTimes = useRef([]); const bhMousePts = useRef([]); const bhScrollDs = useRef([]); const bhLastKey = useRef(0); const bhLastMouse = useRef(0); const onKeyDown = useCallback(e => { const now = performance.now(); if (bhLastKey.current > 0) bhKeyTimes.current.push(now - bhLastKey.current); bhLastKey.current = now; }, []); const onMouseMove = useCallback(e => { const now = performance.now(); if (now - bhLastMouse.current < 50) return; bhLastMouse.current = now; bhMousePts.current.push([e.clientX, e.clientY]); }, []); const onScroll = useCallback(e => { const d = e.target?.scrollTop != null ? Math.abs(e.target.scrollTop) : window.scrollY; bhScrollDs.current.push(d); }, []); const computeScores = useCallback(() => { // Typing entropy (coefficient of variation) let te = 0.5; const kt = bhKeyTimes.current; if (kt.length >= 3) { const mean = kt.reduce((a, b) => a + b, 0) / kt.length; const std = Math.sqrt(kt.reduce((s, v) => s + (v - mean) ** 2, 0) / kt.length); const cv = std / (mean + 1e-6); te = cv < 0.05 ? 0.10 : cv < 0.20 ? 0.30 + cv * 1.5 : cv < 0.90 ? 0.50 + (cv - 0.20) * 0.7 : Math.max(0.10, 1.0 - (cv - 0.90) * 0.8); te = Math.max(0, Math.min(1, te)); } // Mouse linearity let ml = 0.6; const mp = bhMousePts.current; if (mp.length >= 3) { let totalD = 0; for (let i = 1; i < mp.length; i++) { const dx = mp[i][0] - mp[i-1][0], dy = mp[i][1] - mp[i-1][1]; totalD += Math.sqrt(dx * dx + dy * dy); } const dxA = mp[mp.length-1][0] - mp[0][0], dyA = mp[mp.length-1][1] - mp[0][1]; const straightD = Math.sqrt(dxA * dxA + dyA * dyA); ml = Math.max(0, Math.min(1, 1.0 - Math.abs((totalD > 0 ? straightD / totalD : 0) - 0.6) * 1.5)); } // Scroll variance let sv = 0.5; const sd = bhScrollDs.current; if (sd.length >= 2) { const sm = sd.reduce((a, b) => a + b, 0) / sd.length; const ss = Math.sqrt(sd.reduce((s, v) => s + (v - sm) ** 2, 0) / sd.length); sv = Math.min(1, ss / 200); } return { te, ml, sv, lr: Math.max(0, 1.0 - (0.40 * te + 0.35 * ml + 0.25 * sv)) }; }, []); const startCollecting = () => { bhKeyTimes.current = []; bhMousePts.current = []; bhScrollDs.current = []; bhLastKey.current = 0; bhLastMouse.current = 0; document.addEventListener('keydown', onKeyDown); document.addEventListener('mousemove', onMouseMove); document.addEventListener('scroll', onScroll, true); setCollecting(true); setCollStatus('Collecting… (type, move mouse, scroll)'); setBhScores({ te: 0.5, ml: 0.6, sv: 0.5 }); }; const stopCollecting = () => { document.removeEventListener('keydown', onKeyDown); document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('scroll', onScroll, true); const s = computeScores(); setBhScores(s); setCollecting(false); setCollStatus(`Done — Keys:${bhKeyTimes.current.length} Mouse:${bhMousePts.current.length} Scrolls:${bhScrollDs.current.length}`); }; useEffect(() => () => { document.removeEventListener('keydown', onKeyDown); document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('scroll', onScroll, true); }, [onKeyDown, onMouseMove, onScroll]); // ── Cities ──────────────────────────────────────────────────────────────── useEffect(() => { req(`${INTEL}/demo/city-list`, 'GET', null, false).then(r => { if (r.ok && r.data?.cities) setCities(r.data.cities.map(c => c.name)); }); if (getToken()) intelGetTrust(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // ── Trust ───────────────────────────────────────────────────────────────── const intelGetTrust = async () => { if (!getToken()) return; setLoad('trust', true); const r = await req(`${INTEL}/trust-score`); setTrustResp(r); if (r.ok && r.data) { setTrust({ score: r.data.trust_score, label: r.data.label, color: r.data.color }); setTrustHist(r.data.history || []); } setLoad('trust', false); }; const intelVerify = async () => { if (!getToken()) { alert('Login first.'); return; } setLoad('verify', true); const r = await req(`${INTEL}/continuous-verify`, 'POST', {}); setTrustResp(r); if (r.ok) setTrust({ score: r.data.trust_score, label: r.data.label, color: r.data.color }); setLoad('verify', false); }; const intelDropTrust = async () => { if (!getToken()) { alert('Login first (use Quick Login above).'); return; } setLoad('drop', true); const r = await req(`${INTEL}/simulate-trust-drop`, 'POST', { target_score: 25, reason: 'Manual demo drop' }); setTrustResp(r); if (r.ok && r.data) { setTrust({ score: r.data.new_trust, label: r.data.trust_label, color: r.data.trust_color }); } setLoad('drop', false); }; // ── Quick Login ─────────────────────────────────────────────────────────── const quickLogin = async () => { setLoad('ql', true); const r = await req(`${AUTH}/login`, 'POST', { email: 'demo.user@adaptive.demo', password: 'DemoUser@123!' }, false); if (r.ok && r.data?.access_token) { saveToken(r.data.access_token); onTokenSave?.(); setLoginStatus('✅ Logged in as demo.user@adaptive.demo'); await intelGetTrust(); } else { setLoginStatus('❌ Login failed — run Setup in Scenario 1 first.'); } setLoad('ql', false); }; // ── Behavior Send ───────────────────────────────────────────────────────── const sendBehavior = async () => { if (!getToken()) { alert('Login first.'); return; } const s = computeScores(); setBhScores(s); setLoad('beh', true); const r = await req(`${INTEL}/behavior-signal`, 'POST', { typing_entropy: s.te, mouse_linearity: s.ml, scroll_variance: s.sv, local_risk_score: s.lr, }); setBehavResp(r); if (r.ok && r.data?.trust) { setTrust({ score: r.data.trust.score, label: r.data.trust.label, color: r.data.trust.color }); } setLoad('beh', false); }; // ── Travel ──────────────────────────────────────────────────────────────── const checkTravel = async () => { setLoad('travel', true); const r = await req(`${INTEL}/demo/impossible-travel`, 'POST', { from_city: travelFrom, to_city: travelTo, time_gap_hours: parseFloat(travelHours), from_country: '', to_country: '', }, false); setTravelResult(r.ok ? r.data : null); setLoad('travel', false); }; // ── AI Anomaly ──────────────────────────────────────────────────────────── const scoreAnomaly = async () => { setLoad('ai', true); const r = await req(`${INTEL}/demo/anomaly-score`, 'POST', { typing_entropy: parseFloat(aiTyping), mouse_linearity: parseFloat(aiMouse), scroll_variance: parseFloat(aiScroll), hour_normalized: parseFloat(aiHour), failed_attempts_norm: parseFloat(aiFailed), }, false); setAnomResult(r.ok ? r.data : null); setLoad('ai', false); }; // ── Micro-Challenge ─────────────────────────────────────────────────────── const generateChallenge = async () => { if (!getToken()) { const a = Math.floor(Math.random() * 9) + 2, b = Math.floor(Math.random() * 9) + 2; setChallengeId('demo-no-auth'); setChallengeQ(`What is ${a} × ${b} ?`); setChallengeAnswer(''); setChallengeMsg('(Demo mode — login to persist trust changes)'); setShowChallenge(true); return; } setLoad('ch', true); const r = await req(`${INTEL}/micro-challenge/generate`, 'POST', {}); setChallengeResp(r); if (r.ok && r.data?.challenge) { setChallengeId(r.data.challenge.challenge_id); setChallengeQ(r.data.challenge.question); setChallengeAnswer(''); setChallengeMsg(r.data.challenge_needed ? '' : 'ℹ Trust is healthy — showing challenge for demo purposes.'); setShowChallenge(true); } setLoad('ch', false); }; const verifyChallenge = async () => { if (!challengeAnswer.trim()) { alert('Enter your answer.'); return; } if (!challengeId || challengeId === 'demo-no-auth') { setChallengeMsg('✅ Submitted (login to update real trust score).'); setShowChallenge(false); return; } setLoad('chv', true); const r = await req(`${INTEL}/micro-challenge/verify`, 'POST', { challenge_id: challengeId, response: challengeAnswer }); setChallengeResp(r); if (r.ok && r.data) { setChallengeMsg(r.data.reason); if (r.data.correct) { setShowChallenge(false); setTrust({ score: r.data.new_trust, label: r.data.trust_label, color: r.data.trust_color }); } else { setChallengeAnswer(''); } } setLoad('chv', false); }; // ── Explain ─────────────────────────────────────────────────────────────── const explainRisk = async () => { setLoad('exp', true); const r = await req(`${INTEL}/demo/explain`, 'POST', { location_score: parseFloat(expLoc), device_score: parseFloat(expDev), time_score: parseFloat(expTime), velocity_score: parseFloat(expVel), behavior_score: parseFloat(expBeh), security_level: parseInt(expLevel), risk_level: 'medium', }, false); setExplainResult(r.ok ? r.data : null); setLoad('exp', false); }; return (
🧠 Session Intelligence — 8 Advanced Security Features
Continuous Verification • Behavioral Intelligence • Dynamic Trust Score • Micro-Challenges • Explainability • AI Anomaly Detection • Impossible Travel • Privacy-First Design.
{/* Quick Login */} Session Authentication
Protected features require a JWT token. {loginStatus && ( {loginStatus} )}
{/* Trust Score */} Dynamic Trust Score & Continuous Verification
{trustHistory.length > 0 && (
Recent Trust Events
{[...trustHistory].reverse().slice(0, 20).map((e, i) => (
{e.event_type} = 0 ? 'var(--success)' : 'var(--danger)' }}> {e.delta >= 0 ? '+' : ''}{e.delta.toFixed(1)} → {e.score.toFixed(0)}
))}
)}
{/* Behavior Intelligence */} Privacy-First Behavioral Intelligence
🔒 Privacy-First: Keystroke timings, mouse coords and scroll deltas are processed{' '} entirely in-browser. Only the aggregated 0–1 scores are sent to the server.
{collectStatus && {collectStatus}}
{/* Impossible Travel */} Impossible Travel Detector
setTravelHours(e.target.value)} min="0.01" step="0.5" />
{/* AI Anomaly Scorer */} AI Anomaly Scorer {[ { label: 'Typing entropy', val: aiTyping, set: setAiTyping }, { label: 'Mouse linearity', val: aiMouse, set: setAiMouse }, { label: 'Scroll variance', val: aiScroll, set: setAiScroll }, { label: 'Hour normalized', val: aiHour, set: setAiHour }, { label: 'Failed attempts (÷20)', val: aiFailed, set: setAiFailed }, ].map(f => (
{f.label} f.set(e.target.value)} min="0" max="1" step="0.05" style={{ width: 72, textAlign: 'right', padding: '3px 6px' }} />
))}
{/* Micro-Challenges */} Low-Friction Micro-Challenges

Challenges fire only when trust drops below 40 — never interrupts a trusted session.

{showChallenge && (
{challengeQ}
setChallengeAnswer(e.target.value)} placeholder="Your answer…" style={{ width: 160 }} />
{challengeMsg &&
{challengeMsg}
}
)}
{/* Explainability */} Explainable Risk Transparency

Submit factor scores and see exactly which signals contributed and why — with model weights.

{[ { label: '🌍 Location (0-100)', val: expLoc, set: setExpLoc, max: 100 }, { label: '💻 Device', val: expDev, set: setExpDev, max: 100 }, { label: '🕐 Time', val: expTime, set: setExpTime, max: 100 }, { label: '⚡ Velocity', val: expVel, set: setExpVel, max: 100 }, { label: '🧠 Behavior', val: expBeh, set: setExpBeh, max: 100 }, { label: '🔒 Security level (0-4)',val: expLevel, set: setExpLevel, max: 4 }, ].map(f => ( f.set(e.target.value)} min="0" max={f.max} /> ))}
{explainResult && (
🔍 Audit ID: {explainResult.audit_id}  ·  Confidence: {(explainResult.confidence * 100).toFixed(0)}%  ·  Action: {explainResult.action}
{explainResult.summary}
{(explainResult.factors || []).map(f => { const col = f.status === 'anomalous' ? '#dc2626' : '#16a34a'; const bar = Math.min(100, Math.max(0, Math.abs(f.contribution) * 4)); return (
{f.icon} {f.factor} w:{f.model_weight} {f.contribution >= 0 ? '+' : ''}{f.contribution.toFixed(1)}
{f.detail}
); })}
)} {/* Session Audit Trail */} Session Audit Trail (requires login)
); }