/* FoxBot Pro — Auth wizard (design ZIP câblé sur les vrais endpoints).
Flux : auth(login/signup) -> [signup] okx -> telegram -> webauthn -> recap -> app.
Le login entre direct dans l'app. Le signup lance l'onboarding (flag STORE.ONBOARDING
qui retient le wizard monté malgré l'auto-login). Exposé en window.AuthWizard. */
(function () {
const { useState, useEffect, useRef } = React;
const I = {
Mail: () => ,
Lock: () => ,
User: () => ,
Key: () => ,
Eye: () => ,
EyeOff:() => ,
Arrow: () => ,
ArrowL:() => ,
Shield:() => ,
Check: () => ,
Copy: () => ,
Tg: () => ,
Finger:() => ,
};
function Field({ label, type = "text", placeholder, icon, value, onChange, autoComplete, noFill }) {
const [show, setShow] = useState(false);
const [ro, setRo] = useState(!!noFill); // readonly au montage = bloque l'autofill du navigateur
const isPwd = type === "password";
const guard = noFill
? { autoComplete: "off", readOnly: ro, onFocus: () => setRo(false),
name: "fld_" + String(label || "x").replace(/[^a-z]/gi, ""),
"data-lpignore": "true", "data-1p-ignore": "true" }
: { autoComplete };
return (
);
}
const Skip = ({ children, onClick }) => ;
function Stepper({ current }) {
return (
{[1, 2, 3].map((n, i) => (
{n < current ? : n}
{i < 2 && }
))}
);
}
const StepHeader = ({ badge, title, sub }) => (
{badge &&
{badge}
}
{title}
{sub &&
{sub}
}
);
/* ─── STEP 1 : OKX ─── */
function StepOKX({ onNext, onSkip }) {
const [apiKey, setApiKey] = useState("");
const [secret, setSecret] = useState("");
const [pass, setPass] = useState("");
const [mode, setMode] = useState("paper");
const [loading, setLoad] = useState(false);
const [err, setErr] = useState("");
const canSubmit = apiKey.length > 4 && secret.length > 4 && pass.length > 0 && !loading;
async function save(e) {
e.preventDefault();
setErr(""); setLoad(true);
try {
const j = await fetch("/api/okx/credentials", {
method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ api_key: apiKey, secret, passphrase: pass, is_demo: mode === "paper" }),
}).then((r) => r.json());
if (j.ok) onNext("configured");
else setErr(j.msg || "Connexion OKX échouée");
} catch (e2) { setErr(e2.message || "Erreur réseau"); }
setLoad(false);
}
return (
<>
>
);
}
/* ─── STEP 2 : Telegram ─── */
function StepTelegram({ onNext, onSkip }) {
const [code, setCode] = useState(null); // {value, bot}
const [linked, setLink] = useState(false);
const [copied, setCop] = useState(false);
const [loading, setLoad]= useState(false);
const [err, setErr] = useState("");
const pollRef = useRef(null);
async function generate() {
setErr(""); setLoad(true);
try {
const j = await fetch("/api/telegram/link/generate", { method: "POST", credentials: "include" }).then((r) => r.json());
if (j.ok) setCode({ value: j.code, bot: j.bot_username || "Fox_pro75_bot" });
else setErr(j.msg || "Erreur");
} catch (e2) { setErr(e2.message); }
setLoad(false);
}
useEffect(() => {
if (!code || linked) return;
pollRef.current = setInterval(async () => {
try {
const s = await fetch("/api/telegram/link/status", { credentials: "include" }).then((r) => r.json());
if (s && s.linked) { setLink(true); clearInterval(pollRef.current); }
} catch {}
}, 3000);
return () => clearInterval(pollRef.current);
}, [code, linked]);
function copy() { try { navigator.clipboard.writeText(code.value); } catch {} setCop(true); setTimeout(() => setCop(false), 1600); }
return (
<>
{!code ? (
{err &&
{err}
}
onSkip("skipped")}>Configurer plus tard
) : (
TON CODE DE LIAISON
{code.value}
- Ouvre Telegram et cherche @{code.bot}.
- Envoie-lui /link {code.value}
- Reviens ici, la connexion se fait toute seule.
Ouvrir Telegram
{linked ? <> Telegram connecté !> : "En attente de ta connexion Telegram…"}
onSkip("skipped")}>Passer pour l'instant
)}
>
);
}
/* ─── STEP 3 : WebAuthn ─── */
function StepWebAuthn({ onNext, onSkip }) {
const supported = typeof window.isWebauthnSupported === "function" ? window.isWebauthnSupported() : !!window.PublicKeyCredential;
const [state, setState] = useState("idle"); // idle|requesting|enabled|error
const [err, setErr] = useState("");
const ua = navigator.userAgent || "";
const label = /iPhone|iPad/.test(ua) ? "Face ID" : /Mac/.test(ua) ? "Touch ID" : /Windows/.test(ua) ? "Windows Hello" : "biométrie";
async function activate() {
if (!supported) return;
setErr(""); setState("requesting");
try {
const j = await window.fbWebauthnRegister(label);
if (j && j.ok) { setState("enabled"); setTimeout(() => onNext("enabled"), 900); }
else { setState("error"); setErr((j && j.msg) || "Activation impossible"); }
} catch (e2) { setState("error"); setErr(e2.message || "Activation annulée"); }
}
return (
<>
{state === "enabled" ? : }
{!supported && Pas disponible sur cet appareil. Tu peux continuer avec ton mot de passe.
}
{err && {err}
}
onSkip(supported ? "skipped" : "unsupported")}>Plus tard
>
);
}
/* ─── RECAP ─── */
function StepRecap({ progress, onFinish }) {
const items = [
{ k: "account", l: "Compte créé", done: true, sub: "Profil actif" },
{ k: "okx", l: "OKX connecté", done: progress.okx === "configured", sub: progress.okx === "configured" ? "Clés chiffrées AES-256" : "Non configuré" },
{ k: "telegram", l: "Telegram lié", done: progress.telegram === "linked", sub: progress.telegram === "linked" ? "Alertes activées" : "Non lié" },
{ k: "webauthn", l: "Biométrie activée", done: progress.webauthn === "enabled", sub: progress.webauthn === "enabled" ? "Connexion rapide" : "Non activée" },
];
return (
<>
>
);
}
/* ─── AuthCard (login/signup) ─── */
function AuthCard({ onSignupSuccess }) {
const [mode, setMode] = useState("login");
const [first, setFirst] = useState("");
const [last, setLast] = useState("");
const [email, setEmail] = useState("");
const [pwd, setPwd] = useState("");
const [agree, setAgree] = useState(false);
const [loading, setLoad]= useState(false);
const [err, setErr] = useState("");
const isSignup = mode === "signup";
const webauthnOK = typeof window.isWebauthnSupported === "function" ? window.isWebauthnSupported() : !!window.PublicKeyCredential;
async function submit(e) {
e.preventDefault();
setErr(""); setLoad(true);
try {
if (isSignup) {
const display_name = `${first} ${last}`.trim() || undefined;
window.fbSetOnboarding(true); // AVANT le signup : retient le wizard malgré l'auto-login
const res = await window.fbSignup(email, pwd, display_name);
if (res && res.ok) onSignupSuccess();
else { window.fbSetOnboarding(false); setErr((res && res.msg) || "Création échouée"); }
} else {
const res = await window.fbLogin(email, pwd);
if (!res || !res.ok) setErr((res && res.msg) || "Identifiants invalides");
}
} catch (e2) { window.fbSetOnboarding(false); setErr(e2.message || "Erreur réseau"); }
setLoad(false);
}
async function webauthn() {
setErr(""); setLoad(true);
try {
const j = await window.fbWebauthnLogin(email);
if (j && j.ok) { await window.fbAuthRefresh(); await window.fbRefresh(); }
else setErr((j && j.msg) || "Échec biométrique");
} catch (e2) { setErr(e2.message || "Échec biométrique"); }
setLoad(false);
}
const canSubmit = email && pwd && (!isSignup || agree) && !loading;
return (
<>
{isSignup ? "Créez votre compte" : "Re-bonjour, trader."}
{isSignup ? "Vos clés, votre exchange, votre contrôle." : "Connectez-vous pour accéder à vos signaux et positions."}
{!isSignup && webauthnOK && (
<>
OU
>
)}
>
);
}
/* ─── Routeur ─── */
function AuthWizard() {
const [step, setStep] = useState("auth"); // auth|okx|telegram|webauthn|recap
const [progress, setProgress] = useState({ okx: null, telegram: null, webauthn: null });
const prog = (k, v) => setProgress((p) => ({ ...p, [k]: v }));
function finish() {
if (typeof window.fbSetOnboarding === "function") window.fbSetOnboarding(false);
if (typeof window.fbRefresh === "function") window.fbRefresh();
}
return (
{step === "auth" &&
setStep("okx")} />}
{step === "okx" && { prog("okx", s); setStep("telegram"); }} onSkip={(s) => { prog("okx", s); setStep("telegram"); }} />}
{step === "telegram" && { prog("telegram", s); setStep("webauthn"); }} onSkip={(s) => { prog("telegram", s); setStep("webauthn"); }} />}
{step === "webauthn" && { prog("webauthn", s); setStep("recap"); }} onSkip={(s) => { prog("webauthn", s); setStep("recap"); }} />}
{step === "recap" && }
{step === "auth" && (
· CHIFFREMENT AES-256
· PERMISSIONS NO-WITHDRAW
· VOS CLÉS, VOTRE EXCHANGE
)}
);
}
window.AuthWizard = AuthWizard;
})();