UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

625 lines (624 loc) 23.8 kB
'use client'; import { jsx as e, jsxs as t, Fragment as M } from "react/jsx-runtime"; import { useDisclosure as T, Card as A, CardHeader as J, Chip as P, Button as f, Divider as X, CardBody as j, Tabs as Z, Tab as F, Modal as E, ModalContent as V, ModalHeader as R, ModalBody as D, Alert as _, Code as ee, Image as te, Snippet as se } from "@heroui/react"; import u from "react"; import { useConfig as ae } from "../../../hooks/use-config.js"; import { useMFA as B } from "../../../hooks/use-mfa.js"; import { PhoneField as ne } from "../../forms/phone-field.js"; import { VerificationCode as W } from "../../forms/verification-code.js"; function ie({ onSuccess: d, onError: n, onClose: x, isOpen: h }) { const { setupTOTP: b, verifySetup: y } = B(), [v, m] = u.useState("setup"), [l, w] = u.useState(null), [r, p] = u.useState(""), [g, i] = u.useState(!1); u.useEffect(() => { h || (m("setup"), w(null), p("")); }, [h]); const N = async () => { try { i(!0); const a = await b(); w(a), m("verify"); } catch (a) { const o = a instanceof Error ? a.message : "Failed to setup TOTP"; n(o); } finally { i(!1); } }, S = async () => { if (!r || r.length !== 6) { n("Please enter a valid 6-digit code"); return; } try { i(!0), await y("totp", r), d("TOTP authentication enabled successfully"), x(); } catch (a) { const o = a instanceof Error ? a.message : "Failed to verify TOTP"; n(o); } finally { i(!1); } }; return /* @__PURE__ */ t("div", { className: "space-y-4", children: [ v === "setup" && /* @__PURE__ */ t("div", { className: "text-center space-y-4", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-16 h-16 bg-primary/10 rounded-full mx-auto", children: /* @__PURE__ */ e( "svg", { className: "w-8 h-8 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 18h.01M8 21h8a1 1 0 001-1V4a1 1 0 00-1-1H8a1 1 0 00-1 1v16a1 1 0 001 1z" } ) } ) }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Setup Authenticator App" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500 mt-2", children: "We'll help you setup two-factor authentication using an authenticator app like Google Authenticator or Authy." }) ] }), /* @__PURE__ */ t("div", { className: "text-left space-y-3", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-6 h-6 bg-primary text-white text-xs rounded-full flex-shrink-0", children: "1" }), /* @__PURE__ */ e("p", { className: "text-sm", children: "Install an authenticator app on your phone" }) ] }), /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-6 h-6 bg-default-300 text-default-600 text-xs rounded-full flex-shrink-0", children: "2" }), /* @__PURE__ */ e("p", { className: "text-sm", children: "Scan the QR code or enter the secret key" }) ] }), /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-6 h-6 bg-default-300 text-default-600 text-xs rounded-full flex-shrink-0", children: "3" }), /* @__PURE__ */ e("p", { className: "text-sm", children: "Enter the 6-digit code from your app" }) ] }) ] }), /* @__PURE__ */ e( f, { color: "primary", onPress: N, isLoading: g, className: "w-full", children: "Get Started" } ) ] }), v === "verify" && l && /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ t("div", { className: "text-center", children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Scan QR Code" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500 mt-2", children: "Scan this QR code with your authenticator app, or enter the secret key manually." }) ] }), l.qrCode && /* @__PURE__ */ e("div", { className: "flex justify-center", children: /* @__PURE__ */ e("div", { className: "p-4 bg-white rounded-lg border", children: /* @__PURE__ */ e( te, { src: l.qrCode, alt: "QR Code", width: 200, height: 200 } ) }) }), l.secret && /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium mb-2", children: "Or enter this secret key manually:" }), /* @__PURE__ */ e(se, { size: "sm", symbol: "", className: "w-full", children: l.secret }) ] }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium mb-2", children: "Enter the 6-digit code from your app:" }), /* @__PURE__ */ e( W, { length: 6, value: r, onChange: p, onComplete: S } ) ] }), /* @__PURE__ */ t("div", { className: "flex gap-2", children: [ /* @__PURE__ */ e( f, { variant: "light", onPress: () => m("setup"), isDisabled: g, className: "flex-1", children: "Back" } ), /* @__PURE__ */ e( f, { color: "primary", onPress: S, isLoading: g, isDisabled: !r || r.length !== 6, className: "flex-1", children: "Verify & Enable" } ) ] }) ] }) ] }); } function re({ onSuccess: d, onError: n, onClose: x, isOpen: h }) { const { setupSMS: b, verifySetup: y } = B(), [v, m] = u.useState("phone"), [l, w] = u.useState(""), [r, p] = u.useState(""), [g, i] = u.useState(!1); u.useEffect(() => { h || (m("phone"), w(""), p("")); }, [h]); const N = async () => { if (!l) { n("Please enter a valid phone number"); return; } try { i(!0), await b(l), m("verify"); } catch (a) { const o = a instanceof Error ? a.message : "Failed to setup SMS"; n(o); } finally { i(!1); } }, S = async () => { if (!r || r.length !== 6) { n("Please enter a valid 6-digit code"); return; } try { i(!0), await y("sms", r), d("SMS authentication enabled successfully"), x(); } catch (a) { const o = a instanceof Error ? a.message : "Failed to verify SMS"; n(o); } finally { i(!1); } }; return /* @__PURE__ */ t("div", { className: "space-y-4", children: [ v === "phone" && /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ t("div", { className: "text-center", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-16 h-16 bg-primary/10 rounded-full mx-auto mb-4", children: /* @__PURE__ */ e( "svg", { className: "w-8 h-8 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" } ) } ) }), /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Setup SMS Authentication" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500 mt-2", children: "We'll send verification codes to your phone via SMS." }) ] }), /* @__PURE__ */ e( ne, { label: "Phone Number", placeholder: "Enter your phone number", value: l, onChange: w, required: !0 } ), /* @__PURE__ */ e( f, { color: "primary", onPress: N, isLoading: g, isDisabled: !l, className: "w-full", children: "Send Verification Code" } ) ] }), v === "verify" && /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ t("div", { className: "text-center", children: [ /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Verify Phone Number" }), /* @__PURE__ */ t("p", { className: "text-sm text-default-500 mt-2", children: [ "We sent a 6-digit code to ", l, ". Enter it below to verify." ] }) ] }), /* @__PURE__ */ e( W, { length: 6, value: r, onChange: p, onComplete: S } ), /* @__PURE__ */ t("div", { className: "flex gap-2", children: [ /* @__PURE__ */ e( f, { variant: "light", onPress: () => m("phone"), isDisabled: g, className: "flex-1", children: "Back" } ), /* @__PURE__ */ e( f, { color: "primary", onPress: S, isLoading: g, isDisabled: !r || r.length !== 6, className: "flex-1", children: "Verify & Enable" } ) ] }) ] }) ] }); } function le({ codes: d, onRegenerate: n, isLoading: x }) { const [h, b] = u.useState(!1); return /* @__PURE__ */ t("div", { className: "space-y-4", children: [ /* @__PURE__ */ t("div", { className: "text-center", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-16 h-16 bg-warning/10 rounded-full mx-auto mb-4", children: /* @__PURE__ */ e( "svg", { className: "w-8 h-8 text-warning", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" } ) } ) }), /* @__PURE__ */ e("h3", { className: "text-lg font-semibold", children: "Backup Codes" }), /* @__PURE__ */ e("p", { className: "text-sm text-default-500 mt-2", children: "Save these backup codes in a secure place. Each code can only be used once." }) ] }), /* @__PURE__ */ e(_, { color: "warning", variant: "flat", children: /* @__PURE__ */ t("div", { className: "space-y-2", children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Important:" }), /* @__PURE__ */ t("ul", { className: "text-xs space-y-1 ml-4", children: [ /* @__PURE__ */ e("li", { children: "• Store these codes in a secure location" }), /* @__PURE__ */ e("li", { children: "• Each code can only be used once" }), /* @__PURE__ */ e("li", { children: "• Generate new codes when you're running low" }) ] }) ] }) }), d.length > 0 && /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ t("div", { className: "flex items-center justify-between mb-3", children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Your backup codes:" }), /* @__PURE__ */ t( f, { size: "sm", variant: "light", onPress: () => b(!h), children: [ h ? "Hide" : "Show", " Codes" ] } ) ] }), h && /* @__PURE__ */ e("div", { className: "grid grid-cols-2 gap-2", children: d.map((y, v) => /* @__PURE__ */ e(ee, { className: "text-xs", children: y }, v)) }) ] }), /* @__PURE__ */ e( f, { color: "primary", variant: "bordered", onPress: n, isLoading: x, className: "w-full", children: "Generate New Codes" } ) ] }); } function ve({ onSuccess: d, onError: n, showTOTPSetup: x = !0, showSMSSetup: h = !0, showEmailSetup: b = !1, showBackupCodes: y = !0, className: v = "", isDisabled: m = !1, variant: l = "bordered", size: w = "md", defaultMethod: r = "overview", hideMethods: p = [], customMethods: g = [] }) { const { isEnabled: i, mfaMethods: N, backupCodes: S, hasTOTP: a, hasSMS: o, hasBackupCodes: ce, removeMFAMethod: H, regenerateBackupCodes: G, disable: I, isLoading: L } = B(), { components: Q } = ae(), k = T(), C = T(), [q, $] = u.useState(r), O = Q.MFASetup; if (O) return /* @__PURE__ */ e( O, { onSuccess: d, onError: n, showTOTPSetup: x, showSMSSetup: h, showEmailSetup: b, showBackupCodes: y, className: v, isDisabled: m, variant: l, size: w, defaultMethod: r, hideMethods: p, customMethods: g } ); const z = async (s) => { try { await H(s), d?.("MFA method removed successfully"); } catch (c) { const Y = c instanceof Error ? c.message : "Failed to remove MFA method"; n?.(Y); } }, K = async () => { try { await I(), d?.("Two-factor authentication disabled"); } catch (s) { const c = s instanceof Error ? s.message : "Failed to disable MFA"; n?.(c); } }, U = async () => { try { await G(), d?.("Backup codes regenerated successfully"); } catch (s) { const c = s instanceof Error ? s.message : "Failed to regenerate backup codes"; n?.(c); } }; return /* @__PURE__ */ t("div", { className: `space-y-6 ${v}`, children: [ /* @__PURE__ */ t(A, { variant: l, children: [ /* @__PURE__ */ e(J, { children: /* @__PURE__ */ t("div", { className: "flex items-center justify-between w-full", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e( "div", { className: `flex items-center justify-center w-10 h-10 rounded-lg ${i ? "bg-success/10" : "bg-default/10"}`, children: /* @__PURE__ */ e( "svg", { className: `w-5 h-5 ${i ? "text-success" : "text-default-400"}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" } ) } ) } ), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("h4", { className: "text-md font-semibold", children: "Two-Factor Authentication" }), /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ e( P, { size: "sm", color: i ? "success" : "default", variant: "flat", children: i ? "Enabled" : "Disabled" } ), i && /* @__PURE__ */ t("span", { className: "text-xs text-default-500", children: [ N.length, " method", N.length !== 1 ? "s" : "", " configured" ] }) ] }) ] }) ] }), i && /* @__PURE__ */ e( f, { variant: "light", color: "danger", size: "sm", onPress: K, isDisabled: m || L, children: "Disable MFA" } ) ] }) }), !i && /* @__PURE__ */ t(M, { children: [ /* @__PURE__ */ e(X, {}), /* @__PURE__ */ e(j, { children: /* @__PURE__ */ e("p", { className: "text-sm text-default-600", children: "Add an extra layer of security to your account by enabling two-factor authentication. Choose from authenticator apps, SMS, or backup codes." }) }) ] }) ] }), /* @__PURE__ */ t( Z, { selectedKey: q, onSelectionChange: (s) => $(s), variant: "bordered", children: [ /* @__PURE__ */ e(F, { title: "Overview", children: /* @__PURE__ */ t("div", { className: "space-y-4", children: [ x && !p.includes("totp") && /* @__PURE__ */ e(A, { variant: "flat", children: /* @__PURE__ */ e(j, { children: /* @__PURE__ */ t("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-primary/10 rounded-lg", children: /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-primary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 18h.01M8 21h8a1 1 0 001-1V4a1 1 0 00-1-1H8a1 1 0 00-1 1v16a1 1 0 001 1z" } ) } ) }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "Authenticator App" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: "Use an app like Google Authenticator or Authy" }) ] }) ] }), /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [ a && /* @__PURE__ */ e(P, { size: "sm", color: "success", variant: "flat", children: "Enabled" }), /* @__PURE__ */ e( f, { size: "sm", color: a ? "danger" : "primary", variant: a ? "light" : "solid", onPress: a ? () => z( N.find((s) => s.type === "totp")?.id || "" ) : k.onOpen, isDisabled: m, children: a ? "Remove" : "Setup" } ) ] }) ] }) }) }), h && !p.includes("sms") && /* @__PURE__ */ e(A, { variant: "flat", children: /* @__PURE__ */ e(j, { children: /* @__PURE__ */ t("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ t("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ e("div", { className: "flex items-center justify-center w-10 h-10 bg-secondary/10 rounded-lg", children: /* @__PURE__ */ e( "svg", { className: "w-5 h-5 text-secondary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ e( "path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" } ) } ) }), /* @__PURE__ */ t("div", { children: [ /* @__PURE__ */ e("p", { className: "text-sm font-medium", children: "SMS" }), /* @__PURE__ */ e("p", { className: "text-xs text-default-500", children: "Receive codes via text message" }) ] }) ] }), /* @__PURE__ */ t("div", { className: "flex items-center gap-2", children: [ o && /* @__PURE__ */ e(P, { size: "sm", color: "success", variant: "flat", children: "Enabled" }), /* @__PURE__ */ e( f, { size: "sm", color: o ? "danger" : "primary", variant: o ? "light" : "solid", onPress: o ? () => z( N.find((s) => s.type === "sms")?.id || "" ) : C.onOpen, isDisabled: m, children: o ? "Remove" : "Setup" } ) ] }) ] }) }) }) ] }) }, "overview"), y && !p.includes("backup-codes") && /* @__PURE__ */ e(F, { title: "Backup Codes", children: /* @__PURE__ */ e( le, { codes: S, onRegenerate: U, isLoading: L } ) }, "backup-codes"), g.map( (s) => !p.includes(s.key) && /* @__PURE__ */ e(F, { title: s.name, children: s.setupComponent }, s.key) ) ] } ), /* @__PURE__ */ e( E, { isOpen: k.isOpen, onOpenChange: k.onOpenChange, size: "md", placement: "center", hideCloseButton: !0, children: /* @__PURE__ */ e(V, { children: (s) => /* @__PURE__ */ t(M, { children: [ /* @__PURE__ */ e(R, {}), /* @__PURE__ */ e(D, { children: /* @__PURE__ */ e( ie, { onSuccess: (c) => { d?.(c), s(); }, onError: (c) => n?.(c), onClose: s, isOpen: k.isOpen } ) }) ] }) }) } ), /* @__PURE__ */ e( E, { isOpen: C.isOpen, onOpenChange: C.onOpenChange, size: "md", placement: "center", hideCloseButton: !0, children: /* @__PURE__ */ e(V, { children: (s) => /* @__PURE__ */ t(M, { children: [ /* @__PURE__ */ e(R, {}), /* @__PURE__ */ e(D, { children: /* @__PURE__ */ e( re, { onSuccess: (c) => { d?.(c), s(); }, onError: (c) => n?.(c), onClose: s, isOpen: C.isOpen } ) }) ] }) }) } ) ] }); } export { ve as MFASetup }; //# sourceMappingURL=mfa-setup.js.map