UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

383 lines (382 loc) 9.46 kB
import { useMemo as l, useState as b, useCallback as d, useEffect as V } from "react"; import { useAuth as X } from "./use-auth.js"; import { useConfig as Y } from "../provider/config-provider.js"; const te = { totp: { name: "Authenticator App", description: "Use an authenticator app like Google Authenticator or Authy", icon: "📱", setupSteps: [ "Install an authenticator app on your phone", "Scan the QR code or enter the secret key", "Enter the 6-digit code from your app" ] }, sms: { name: "SMS", description: "Receive codes via text message", icon: "💬", setupSteps: [ "Enter your phone number", "Wait for the verification code", "Enter the code to confirm" ] }, email: { name: "Email", description: "Receive codes via email", icon: "✉️", setupSteps: [ "Confirm your email address", "Wait for the verification code", "Enter the code to confirm" ] }, webauthn: { name: "Security Key", description: "Use a hardware security key or biometric authentication", icon: "🔐", setupSteps: [ "Insert your security key or prepare biometric authentication", "Follow your browser's authentication prompts", "Confirm the registration" ] }, backup_codes: { name: "Backup Codes", description: "Single-use codes for emergency access", icon: "🔢", setupSteps: [ "Save these codes in a secure location", "Each code can only be used once", "Generate new codes when running low" ] } }; function R() { const { user: a, session: w, reload: u, userType: f, sdk: t } = X(), { apiUrl: m, publishableKey: M, features: i } = Y(), [n, U] = b([]), [g, E] = b([]), [T, s] = b(!1), [O, c] = b(null), h = l(() => i.mfa, [i.mfa]), o = d((e) => { const r = { code: e.code || "UNKNOWN_ERROR", message: e.message || "An unknown error occurred", details: e.details, field: e.field }; throw c(r), r; }, []), p = d(async () => { if (!(!t.user || !a || !h)) try { s(!0), c(null); const e = await t.user.getMFAMethods({ orgId: t.user.getOrganizationId(), userId: t.user.getUserData() }); if (U(e.data || []), a.mfaEnabled) try { const r = await t.user.getBackupCodes(); E(r.codes || []); } catch (r) { console.warn("Could not load backup codes:", r); } } catch (e) { console.error("Failed to load MFA data:", e), c({ code: "MFA_LOAD_FAILED", message: "Failed to load MFA data" }); } finally { s(!1); } }, [t.user, a, h]); V(() => { p(); }, [p]); const B = d(async () => { if (!t.user) throw new Error("User not authenticated"); if (!h) throw new Error("MFA not available"); try { s(!0), c(null); const e = { method: "totp" }, r = await t.user.setupMFA(e); return { method: "totp", qrCode: r.qrCode, secret: r.secret, backupCodes: r.backupCodes, verificationRequired: !0 }; } catch (e) { return o(e); } finally { s(!1); } }, [t.user, h, o]), L = d(async (e) => { if (!t.user) throw new Error("User not authenticated"); if (!h) throw new Error("MFA not available"); try { s(!0), c(null); const r = { method: "sms", phoneNumber: e }, A = await t.user.setupMFA(r); return { method: "sms", verificationRequired: !0 }; } catch (r) { return o(r); } finally { s(!1); } }, [t.user, h, o]), I = d(async (e) => { if (!t.user) throw new Error("User not authenticated"); if (!h) throw new Error("MFA not available"); try { s(!0), c(null); const r = { method: "email", email: e || a?.primaryEmailAddress }, A = await t.user.setupMFA(r); return { method: "email", verificationRequired: !0 }; } catch (r) { return o(r); } finally { s(!1); } }, [t.user, a, h, o]), P = d(async () => { if (!h) throw new Error("MFA not available"); try { s(!0), c(null); const e = { method: "webauthn" }; return { method: "webauthn", challenge: (await t.auth.setupMFA(e)).challenge, verificationRequired: !0 }; } catch (e) { return o(e); } finally { s(!1); } }, [t.user, h, o]), _ = d(async (e, r, A) => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null); const y = { method: e, code: r, methodId: A, generateBackupCodes: !1 }, q = await t.user.verifyMFASetup(y); return await p(), await u(), q.method; } catch (y) { return o(y); } finally { s(!1); } }, [t.user, p, u, o]), N = d(async (e, r, A) => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null); const y = { method: e, code: r, mfaToken: A, context: "login" }; return await t.auth.verifyMFA(y); } catch (y) { return o(y); } finally { s(!1); } }, [t.user, o]), D = d(async (e) => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null), await t.user.removeMFAMethod(e), await p(), await u(); } catch (r) { o(r); } finally { s(!1); } }, [t.user, p, u, o]), W = d(async (e) => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null), await t.user.setPrimaryMFAMethod(e), await p(); } catch (r) { o(r); } finally { s(!1); } }, [t.user, p, o]), x = d(async () => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null); const e = await t.user.regenerateMFABackupCodes(); return E(e.codes), e.codes; } catch (e) { return o(e); } finally { s(!1); } }, [t.user, o]), G = l(() => a?.mfaEnabled || !1, [a]), K = l(() => !1, []), z = l(() => n.find((e) => e.isPrimary) || null, [n]), v = l( () => n.some((e) => e.type === "totp"), [n] ), F = l( () => n.some((e) => e.type === "sms"), [n] ), S = l( () => n.some((e) => e.type === "email"), [n] ), k = l( () => n.some((e) => e.type === "webauthn"), [n] ), C = l( () => g.length > 0, [g] ), H = l(() => { const e = []; return v && e.push("totp"), F && e.push("sms"), S && e.push("email"), k && e.push("webauthn"), C && e.push("backup_codes"), e; }, [v, F, S, k, C]), Q = d(async () => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null), await t.user.disableMFA(), U([]), E([]), await u(); } catch (e) { o(e); } finally { s(!1); } }, [t.user, u, o]), j = d(async () => { if (!t.user) throw new Error("User not authenticated"); try { s(!0), c(null), await t.user.enableMFA(), await u(); } catch (e) { o(e); } finally { s(!1); } }, [t.user, u, o]), J = d(async () => { await p(); }, [p]); return { // MFA state mfaMethods: n, isEnabled: G, isRequired: K, primaryMethod: z, backupCodes: g, isLoaded: !!a && h, isLoading: T, error: O, // MFA setup setupTOTP: B, setupSMS: L, setupEmail: I, setupWebAuthn: P, // MFA verification verifySetup: _, verifyMFA: N, // Method management removeMFAMethod: D, setPrimaryMethod: W, regenerateBackupCodes: x, // MFA status checking hasTOTP: v, hasSMS: F, hasEmail: S, hasWebAuthn: k, hasBackupCodes: C, // Method availability availableMethods: H, // Convenience methods disable: Q, enable: j, refreshMethods: J }; } function re() { const { setupTOTP: a, verifySetup: w, hasTOTP: u, mfaMethods: f, removeMFAMethod: t, isLoading: m, error: M } = R(), i = l( () => f.find((n) => n.type === "totp"), [f] ); return { isEnabled: u, method: i, setup: a, verify: (n) => w("totp", n), remove: i ? () => t(i.id) : void 0, isLoading: m, error: M }; } function se() { const { setupSMS: a, verifySetup: w, hasSMS: u, mfaMethods: f, removeMFAMethod: t, isLoading: m, error: M } = R(), i = l( () => f.find((n) => n.type === "sms"), [f] ); return { isEnabled: u, method: i, setup: a, verify: (n) => w("sms", n), remove: i ? () => t(i.id) : void 0, phoneNumber: i?.phoneNumber || null, isLoading: m, error: M }; } function oe() { const { backupCodes: a, regenerateBackupCodes: w, hasBackupCodes: u, isLoading: f, error: t } = R(), m = l( () => a.filter((i) => !i.used), [a] ), M = l( () => a.filter((i) => i.used), [a] ); return { codes: a, unusedCodes: m, usedCodes: M, hasBackupCodes: u, regenerate: w, remainingCodes: m.length, totalCodes: a.length, isRunningLow: m.length <= 2, isLoading: f, error: t }; } export { te as MFA_METHOD_CONFIGS, oe as useBackupCodes, R as useMFA, se as useSMSMFA, re as useTOTP }; //# sourceMappingURL=use-mfa.js.map