auth / client /src /tabs /Scenario1Tab.jsx
Piyush1225's picture
UPDATE: UI and client assets
808332c
import { useState } from 'react';
import { req, saveToken, ENDPOINTS } from '../api';
import { Card, CardHeader, Callout, FormGroup, ResponseBox, StepBar, Tag } from '../components/ui';
import { RiskVizCard } from '../components/RiskViz';
const DEMO = ENDPOINTS.DEMO;
const STEPS = [
{ label: 'Setup', sub: 'Create demo user' },
{ label: 'Normal Login', sub: 'Known context' },
{ label: 'Suspicious Login', sub: 'Unknown context' },
{ label: 'Verify Challenge', sub: 'Step-up auth' },
];
export default function Scenario1Tab({ onTokenSave }) {
const [step, setStep] = useState(-1);
const [setupResp, setSetupResp] = useState(null);
const [normalResp, setNormalResp] = useState(null);
const [suspResp, setSuspResp] = useState(null);
const [challengeResp, setChallengeResp] = useState(null);
const [riskData, setRiskData] = useState(null);
const [challengeId, setChallengeId] = useState('');
const [challengeCode, setChallengeCode] = useState('');
const [showChallenge, setShowChallenge] = useState(false);
const [loading, setLoading] = useState({});
const setLoad = (k, v) => setLoading(prev => ({ ...prev, [k]: v }));
const setupDemo = async (reset) => {
setLoad('setup', true);
const r = await req(`${DEMO}/setup?reset=${reset}`, 'POST', null, false);
setSetupResp(r);
if (r.ok) setStep(0);
setLoad('setup', false);
};
const checkState = async () => {
setLoad('setup', true);
const r = await req(`${DEMO}/state`, 'GET', null, false);
setSetupResp(r);
setLoad('setup', false);
};
const doNormalLogin = async () => {
setLoad('normal', true);
const r = await req(`${DEMO}/scenario1/normal-login`, 'POST', null, false);
setNormalResp(r);
if (r.ok && r.data) {
const fd = r.data.framework_decision || {};
const t = fd.access_token || r.data.access_token;
if (t) { saveToken(t); onTokenSave?.(); }
setRiskData({ decision: fd, notes: r.data.what_the_framework_checked });
setStep(s => Math.max(s, 1));
setShowChallenge(false);
}
setLoad('normal', false);
};
const doSuspiciousLogin = async () => {
setLoad('susp', true);
const r = await req(`${DEMO}/scenario1/suspicious-login`, 'POST', null, false);
setSuspResp(r);
if (r.ok && r.data) {
const fd = r.data.framework_decision || {};
setRiskData({ decision: fd, notes: r.data.anomalies_triggered });
setStep(s => Math.max(s, 2));
if (fd.status === 'challenge_required' && fd.challenge_id) {
setChallengeId(fd.challenge_id);
setShowChallenge(true);
} else {
setShowChallenge(false);
}
}
setLoad('susp', false);
};
const doCompleteChallenge = async () => {
if (!challengeId || !challengeCode) { alert('Enter challenge ID and code.'); return; }
setLoad('challenge', true);
const url = `${DEMO}/scenario1/complete-challenge?challenge_id=${encodeURIComponent(challengeId)}&code=${encodeURIComponent(challengeCode)}`;
const r = await req(url, 'POST', null, false);
setChallengeResp(r);
if (r.ok) {
const t = r.data?.result?.access_token;
if (t) { saveToken(t); onTokenSave?.(); }
setStep(3);
}
setLoad('challenge', false);
};
return (
<div>
<Callout type="info">
<strong>Scenario 1 — User Behaviour Anomaly Detection</strong><br />
The demo user has <strong>30 days of normal login history</strong> from New York on Windows Chrome
(Mon–Fri, 8AM–5PM). We show how the framework reacts when the <em>same password</em> is used
from a completely different context.
</Callout>
<StepBar steps={STEPS} current={step} />
{/* Step 0 — Setup */}
<Card>
<CardHeader icon="⚙️">Step 0 – Setup Demo Environment</CardHeader>
<p className="text-muted text-sm mb-3">
Creates the demo user with a realistic 30-day behavioral profile (15 logins from a
trusted IP, device, and time window).
</p>
<div className="flex gap-2 flex-wrap">
<button className="btn btn-primary" onClick={() => setupDemo(false)} disabled={loading.setup}>
🔧 Setup Demo
</button>
<button className="btn btn-warn" onClick={() => setupDemo(true)} disabled={loading.setup}>
🔄 Reset &amp; Re-setup
</button>
<button className="btn btn-ghost" onClick={checkState} disabled={loading.setup}>
📊 Check State
</button>
</div>
<ResponseBox result={setupResp} />
</Card>
{/* Step 1 & 2 — Compare */}
<div className="compare-grid">
{/* Normal */}
<div className="compare-side success">
<div className="compare-title success">✅ Normal Context</div>
<div className="context-list">
<div><Tag>IP</Tag> 203.0.113.10 (known)</div>
<div><Tag>Location</Tag> New York, US</div>
<div><Tag>Device</Tag> Windows Chrome</div>
<div><Tag>Time</Tag> Business hours</div>
<div><Tag>History</Tag> 15 logins seen</div>
</div>
<button
className="btn btn-success btn-full mt-3"
onClick={doNormalLogin}
disabled={loading.normal}
>
{loading.normal ? 'Logging in…' : '▶ Run Normal Login'}
</button>
<ResponseBox result={normalResp} />
</div>
{/* Suspicious */}
<div className="compare-side danger">
<div className="compare-title danger">🚩 Suspicious Context</div>
<div className="context-list">
<div><Tag>IP</Tag> 198.51.100.55 (new!)</div>
<div><Tag>Location</Tag> Moscow, Russia</div>
<div><Tag>Device</Tag> iPhone Safari (new!)</div>
<div><Tag>Time</Tag> Same password</div>
<div><Tag>History</Tag> 0 logins from here</div>
</div>
<button
className="btn btn-danger btn-full mt-3"
onClick={doSuspiciousLogin}
disabled={loading.susp}
>
{loading.susp ? 'Logging in…' : '▶ Run Suspicious Login'}
</button>
<ResponseBox result={suspResp} />
</div>
</div>
{/* Risk Visualization */}
{riskData && (
<RiskVizCard decision={riskData.decision} notes={riskData.notes} />
)}
{/* Step 3 — Challenge */}
{showChallenge && (
<Card>
<CardHeader icon="🔐">Step 3 – Complete Step-up Challenge</CardHeader>
<Callout type="warn">
The framework triggered a challenge because of the suspicious context. In a live deployment
this sends a real email. In the demo, use code <strong>000000</strong>.
</Callout>
<div className="grid-2" style={{ alignItems: 'start' }}>
<div>
<FormGroup label="Challenge ID">
<input value={challengeId} readOnly placeholder="Auto-filled from step 2" />
</FormGroup>
<FormGroup label="Verification Code">
<input
value={challengeCode}
onChange={e => setChallengeCode(e.target.value)}
placeholder="Enter code (000000 for demo)"
/>
</FormGroup>
<button
className="btn btn-primary btn-full"
onClick={doCompleteChallenge}
disabled={loading.challenge}
>
{loading.challenge ? 'Verifying…' : '✅ Verify & Complete Login'}
</button>
</div>
<div className="text-sm text-2">
<p className="font-600">Why was this required?</p>
<ul className="mt-2 ml-4" style={{ lineHeight: 2 }}>
<li>Unknown IP address</li>
<li>New device fingerprint</li>
<li>Geographic location changed</li>
<li>Security Level ≥ 2 → challenge required</li>
</ul>
</div>
</div>
<ResponseBox result={challengeResp} />
</Card>
)}
</div>
);
}