@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
383 lines (382 loc) • 9.46 kB
JavaScript
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