@authowl/react 0.21.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -356,104 +356,19 @@ function PasskeyButton({ redirectTo, onSignedIn }) {
356
356
  }
357
357
 
358
358
  // src/components/ForgotPassword.tsx
359
- import * as React5 from "react";
360
-
361
- // src/components/AuthChallenge.tsx
362
359
  import * as React4 from "react";
363
360
 
364
- // src/components/Turnstile.tsx
361
+ // src/components/AuthChallenge.tsx
365
362
  import * as React3 from "react";
366
- import { jsx as jsx4 } from "react/jsx-runtime";
367
- var SCRIPT_URL = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
368
- var SCRIPT_LOAD_TIMEOUT_MS = 1e4;
369
- var LOCAL_TURNSTILE_TEST_TOKEN = "XXXX.DUMMY.TOKEN.XXXX";
370
- var loader = null;
371
- function loadTurnstile() {
372
- if (window.turnstile) return Promise.resolve(window.turnstile);
373
- if (loader) return loader;
374
- loader = new Promise((resolve, reject) => {
375
- const existing = document.querySelector(`script[src="${SCRIPT_URL}"]`);
376
- const script = existing ?? document.createElement("script");
377
- const cleanup = () => {
378
- clearTimeout(timeout);
379
- script.removeEventListener("load", loaded);
380
- script.removeEventListener("error", failed);
381
- };
382
- const loaded = () => {
383
- cleanup();
384
- if (window.turnstile) {
385
- resolve(window.turnstile);
386
- } else {
387
- reject(new Error("Turnstile unavailable"));
388
- }
389
- };
390
- const failed = () => {
391
- cleanup();
392
- if (!existing) script.remove();
393
- reject(new Error("Turnstile failed to load"));
394
- };
395
- const timeout = setTimeout(failed, SCRIPT_LOAD_TIMEOUT_MS);
396
- script.addEventListener("load", loaded, { once: true });
397
- script.addEventListener("error", failed, { once: true });
398
- if (!existing) {
399
- script.src = SCRIPT_URL;
400
- script.async = true;
401
- script.defer = true;
402
- const nonce = document.querySelector("script[nonce]")?.nonce;
403
- if (nonce) script.nonce = nonce;
404
- document.head.appendChild(script);
405
- }
406
- }).catch((error) => {
407
- loader = null;
408
- throw error;
409
- });
410
- return loader;
411
- }
412
- function Turnstile({
413
- siteKey,
414
- theme,
415
- onToken,
416
- onUnavailable
417
- }) {
418
- const container = React3.useRef(null);
419
- const callbacks = React3.useRef({ onToken, onUnavailable });
420
- callbacks.current = { onToken, onUnavailable };
421
- React3.useEffect(() => {
422
- callbacks.current.onToken(null);
423
- if (!siteKey) {
424
- callbacks.current.onToken(LOCAL_TURNSTILE_TEST_TOKEN);
425
- return;
426
- }
427
- let active = true;
428
- let widget = null;
429
- void loadTurnstile().then((api) => {
430
- if (!active || !container.current) return;
431
- const id = api.render(container.current, {
432
- sitekey: siteKey,
433
- theme: theme === "system" ? "auto" : theme,
434
- size: "flexible",
435
- callback: (token) => callbacks.current.onToken(token),
436
- "expired-callback": () => callbacks.current.onToken(null),
437
- "error-callback": () => {
438
- callbacks.current.onToken(null);
439
- callbacks.current.onUnavailable();
440
- }
441
- });
442
- widget = { api, id };
443
- }).catch(() => {
444
- if (active) callbacks.current.onUnavailable();
445
- });
446
- return () => {
447
- active = false;
448
- if (widget) widget.api.remove(widget.id);
449
- };
450
- }, [siteKey, theme]);
451
- if (!siteKey) return null;
452
- return /* @__PURE__ */ jsx4("div", { className: "ba-turnstile", ref: container, "data-testid": "phoneotp-turnstile" });
363
+
364
+ // src/components/captcha-provider-ids.ts
365
+ var CAPTCHA_PROVIDER_IDS = ["turnstile", "hcaptcha", "recaptcha-v2"];
366
+ function isSupportedCaptchaProvider(provider) {
367
+ return CAPTCHA_PROVIDER_IDS.includes(provider);
453
368
  }
454
369
 
455
370
  // src/components/AuthChallenge.tsx
456
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
371
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
457
372
  var AUTH_CHALLENGE_ACTIONS = {
458
373
  signUp: "auth_signup",
459
374
  signIn: "auth_signin",
@@ -468,38 +383,53 @@ var challengeError = {
468
383
  code: "BOT_CHALLENGE_FAILED",
469
384
  message: "Human verification failed."
470
385
  };
471
- function turnstileTheme(root, fallback) {
386
+ function captchaTheme(root, fallback) {
472
387
  const rendered = root?.dataset.authowlTheme;
473
388
  if (rendered === "light" || rendered === "dark") return rendered;
474
389
  if (fallback === "light" || fallback === "dark") return fallback;
475
390
  return "auto";
476
391
  }
477
392
  function useAuthChallenge() {
478
- const { config, isLoading } = usePublicConfig();
393
+ const { config, isLoading, retry } = usePublicConfig();
479
394
  const t = useT();
480
- const container = React4.useRef(null);
481
- const active = React4.useRef(null);
482
- const [status, setStatus] = React4.useState("idle");
483
- const siteKey = config?.authTurnstileSiteKey ?? null;
484
- const removeActive = React4.useCallback((reason) => {
395
+ const container = React3.useRef(null);
396
+ const active = React3.useRef(null);
397
+ const [status, setStatus] = React3.useState("idle");
398
+ const captcha = config?.captcha ?? null;
399
+ const providerSupported = captcha ? isSupportedCaptchaProvider(captcha.provider) : false;
400
+ const unavailableProvider = captcha && !providerSupported ? captcha.provider : null;
401
+ const refetched = React3.useRef(null);
402
+ const refetchIfConfigMayBeStale = React3.useCallback(() => {
403
+ if (!config || refetched.current === config) return;
404
+ refetched.current = config;
405
+ retry();
406
+ }, [config, retry]);
407
+ const removeActive = React3.useCallback((reason) => {
485
408
  const current = active.current;
486
409
  active.current = null;
487
410
  if (!current) return;
488
- current.api.remove(current.id);
411
+ current.adapter.teardown(current.api, current.id);
489
412
  if (reason) current.reject(reason);
490
413
  }, []);
491
- React4.useEffect(
492
- () => () => removeActive(new Error("Turnstile challenge was cancelled")),
414
+ React3.useEffect(
415
+ () => () => removeActive(new Error("Captcha challenge was cancelled")),
493
416
  [removeActive]
494
417
  );
495
- const tokenFor = React4.useCallback(
418
+ const tokenFor = React3.useCallback(
496
419
  async (action) => {
497
- if (!siteKey) return null;
420
+ if (!captcha) return null;
421
+ if (!providerSupported) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
498
422
  setStatus("checking");
499
- removeActive(new Error("Turnstile challenge was replaced"));
423
+ removeActive(new Error("Captcha challenge was replaced"));
500
424
  try {
501
- const api = await loadTurnstile();
502
- if (!container.current) throw new Error("Turnstile container unavailable");
425
+ const [{ loadCaptcha }, { captchaAdapterFor }] = await Promise.all([
426
+ import("./captcha-loader-VNULARCZ.js"),
427
+ import("./captcha-providers-STYFL25P.js")
428
+ ]);
429
+ const adapter = captchaAdapterFor(captcha.provider);
430
+ if (!adapter) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
431
+ const api = await loadCaptcha(adapter);
432
+ if (!container.current) throw new Error("Captcha container unavailable");
503
433
  return await new Promise((resolve, reject) => {
504
434
  let settled = false;
505
435
  const finish = (outcome) => {
@@ -507,27 +437,29 @@ function useAuthChallenge() {
507
437
  settled = true;
508
438
  const current = active.current;
509
439
  active.current = null;
510
- if (current) current.api.remove(current.id);
440
+ if (current) current.adapter.teardown(current.api, current.id);
511
441
  if ("token" in outcome) resolve(outcome.token);
512
442
  else reject(outcome.error);
513
443
  };
514
- const fail = () => finish({ error: new Error("Turnstile challenge failed") });
444
+ const fail = () => finish({ error: new Error("Captcha challenge failed") });
515
445
  const root = container.current.closest(".authowl-root");
446
+ const optionalFailureCallbacks = captcha.provider === "turnstile" ? {
447
+ "timeout-callback": fail,
448
+ "unsupported-callback": fail
449
+ } : {};
516
450
  const id = api.render(container.current, {
517
- sitekey: siteKey,
518
- theme: turnstileTheme(root, config?.branding?.theme),
519
- action,
520
- execution: "execute",
521
- appearance: "interaction-only",
522
- size: "flexible",
523
- language: root?.dataset.authowlLocale ?? config?.locale,
451
+ ...adapter.invisibleRenderOptions({
452
+ siteKey: captcha.siteKey,
453
+ theme: captchaTheme(root, config?.branding?.theme),
454
+ action,
455
+ language: root?.dataset.authowlLocale ?? config?.locale
456
+ }),
524
457
  callback: (token) => finish({ token }),
525
458
  "expired-callback": fail,
526
459
  "error-callback": fail,
527
- "timeout-callback": fail,
528
- "unsupported-callback": fail
460
+ ...optionalFailureCallbacks
529
461
  });
530
- active.current = { api, id, reject };
462
+ active.current = { adapter, api, id, reject };
531
463
  try {
532
464
  api.execute(id);
533
465
  } catch {
@@ -538,9 +470,9 @@ function useAuthChallenge() {
538
470
  setStatus("idle");
539
471
  }
540
472
  },
541
- [config?.branding?.theme, config?.locale, removeActive, siteKey]
473
+ [captcha, config?.branding?.theme, config?.locale, providerSupported, removeActive]
542
474
  );
543
- const run = React4.useCallback(
475
+ const run = React3.useCallback(
544
476
  async (action, request) => {
545
477
  if (isLoading) {
546
478
  return {
@@ -550,37 +482,40 @@ function useAuthChallenge() {
550
482
  }
551
483
  try {
552
484
  const token = await tokenFor(action);
553
- return await request(token ? { authChallengeToken: token } : void 0);
485
+ const result = await request(token ? { authChallengeToken: token } : void 0);
486
+ if (result?.error?.code === challengeError.code) refetchIfConfigMayBeStale();
487
+ return result;
554
488
  } catch {
555
489
  setStatus("failed");
490
+ refetchIfConfigMayBeStale();
556
491
  return {
557
492
  data: null,
558
493
  error: challengeError
559
494
  };
560
495
  }
561
496
  },
562
- [isLoading, tokenFor]
497
+ [isLoading, refetchIfConfigMayBeStale, tokenFor]
563
498
  );
564
- const control = siteKey ? /* @__PURE__ */ jsxs4("div", { className: "ba-auth-challenge", "data-testid": "auth-challenge", children: [
565
- /* @__PURE__ */ jsx5("div", { className: "ba-turnstile", ref: container }),
566
- /* @__PURE__ */ jsx5("p", { className: "ba-sr-only", role: "status", "aria-live": "polite", children: status === "checking" ? t("authChallenge.checking") : status === "failed" ? t("authChallenge.error.failed") : "" })
499
+ const control = captcha ? /* @__PURE__ */ jsxs4("div", { className: "ba-auth-challenge", "data-testid": "auth-challenge", children: [
500
+ providerSupported ? /* @__PURE__ */ jsx4("div", { className: "ba-turnstile", ref: container }) : null,
501
+ unavailableProvider ? /* @__PURE__ */ jsx4(FormError, { "data-testid": "auth-challenge-unsupported", children: t("authChallenge.error.unsupportedProvider", { provider: unavailableProvider }) }) : /* @__PURE__ */ jsx4("p", { className: "ba-sr-only", role: "status", "aria-live": "polite", children: status === "checking" ? t("authChallenge.checking") : status === "failed" ? t("authChallenge.error.failed") : "" })
567
502
  ] }) : null;
568
503
  return { run, control, configPending: isLoading };
569
504
  }
570
505
 
571
506
  // src/components/ForgotPassword.tsx
572
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
507
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
573
508
  function ForgotPassword({ resetPasswordUrl, onBack }) {
574
509
  const t = useT();
575
510
  const { requestPasswordReset } = usePasswordReset();
576
511
  const { pending, error, run } = useSubmitAction();
577
512
  const authChallenge = useAuthChallenge();
578
- const [email, setEmail] = React5.useState("");
579
- const [sent, setSent] = React5.useState(false);
513
+ const [email, setEmail] = React4.useState("");
514
+ const [sent, setSent] = React4.useState(false);
580
515
  if (sent) {
581
516
  return /* @__PURE__ */ jsxs5("div", { className: "ba-fields", "data-testid": "forgot-sent", children: [
582
- /* @__PURE__ */ jsx6("p", { className: "ba-muted", children: richMessage(t("forgotPassword.sent"), { email: /* @__PURE__ */ jsx6(Bidi, { children: email }) }) }),
583
- onBack && /* @__PURE__ */ jsx6("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
517
+ /* @__PURE__ */ jsx5("p", { className: "ba-muted", children: richMessage(t("forgotPassword.sent"), { email: /* @__PURE__ */ jsx5(Bidi, { children: email }) }) }),
518
+ onBack && /* @__PURE__ */ jsx5("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
584
519
  ] });
585
520
  }
586
521
  return /* @__PURE__ */ jsxs5(
@@ -599,7 +534,7 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
599
534
  children: [
600
535
  /* @__PURE__ */ jsxs5("label", { className: "ba-label", children: [
601
536
  t("common.emailLabel"),
602
- /* @__PURE__ */ jsx6(
537
+ /* @__PURE__ */ jsx5(
603
538
  "input",
604
539
  {
605
540
  className: "ba-input",
@@ -612,25 +547,25 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
612
547
  )
613
548
  ] }),
614
549
  authChallenge.control,
615
- /* @__PURE__ */ jsx6(FormError, { children: error }),
616
- /* @__PURE__ */ jsx6(
550
+ /* @__PURE__ */ jsx5(FormError, { children: error }),
551
+ /* @__PURE__ */ jsx5(
617
552
  "button",
618
553
  {
619
554
  className: "ba-button",
620
555
  type: "submit",
621
556
  disabled: pending || authChallenge.configPending,
622
557
  "aria-busy": pending || void 0,
623
- children: /* @__PURE__ */ jsx6(Busy, { busy: pending, label: t("common.sending"), children: t("forgotPassword.submit") })
558
+ children: /* @__PURE__ */ jsx5(Busy, { busy: pending, label: t("common.sending"), children: t("forgotPassword.submit") })
624
559
  }
625
560
  ),
626
- onBack && /* @__PURE__ */ jsx6("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
561
+ onBack && /* @__PURE__ */ jsx5("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
627
562
  ]
628
563
  }
629
564
  );
630
565
  }
631
566
 
632
567
  // src/components/OtpCodeForm.tsx
633
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
568
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
634
569
  function OtpCodeForm({
635
570
  email,
636
571
  code,
@@ -653,8 +588,8 @@ function OtpCodeForm({
653
588
  },
654
589
  children: [
655
590
  /* @__PURE__ */ jsxs6("label", { className: "ba-label", children: [
656
- richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */ jsx7(Bidi, { children: email }) }),
657
- /* @__PURE__ */ jsx7(
591
+ richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */ jsx6(Bidi, { children: email }) }),
592
+ /* @__PURE__ */ jsx6(
658
593
  "input",
659
594
  {
660
595
  className: "ba-input",
@@ -667,24 +602,24 @@ function OtpCodeForm({
667
602
  }
668
603
  )
669
604
  ] }),
670
- /* @__PURE__ */ jsx7(FormError, { children: error }),
671
- /* @__PURE__ */ jsx7("button", { className: "ba-button", type: "submit", disabled: pending, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx7(Busy, { busy: pending, label: t("common.verifying"), children: t("emailOtp.verifySubmit") }) }),
672
- /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", onClick: onChangeEmail, children: t("emailOtp.changeEmail") })
605
+ /* @__PURE__ */ jsx6(FormError, { children: error }),
606
+ /* @__PURE__ */ jsx6("button", { className: "ba-button", type: "submit", disabled: pending, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx6(Busy, { busy: pending, label: t("common.verifying"), children: t("emailOtp.verifySubmit") }) }),
607
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "ba-link-button", onClick: onChangeEmail, children: t("emailOtp.changeEmail") })
673
608
  ]
674
609
  }
675
610
  );
676
611
  }
677
612
 
678
613
  // src/components/MFAChallenge.tsx
679
- import * as React6 from "react";
680
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
614
+ import * as React5 from "react";
615
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
681
616
  function MFAChallenge({ onVerified, allowTrustDevice = true }) {
682
617
  const t = useT();
683
618
  const { verifyTotp, verifyBackupCode, sendOtp, verifyOtp } = useMFA();
684
619
  const { pending, error, setError, run } = useSubmitAction();
685
- const [mode, setMode] = React6.useState("totp");
686
- const [code, setCode] = React6.useState("");
687
- const [trustDevice, setTrustDevice] = React6.useState(false);
620
+ const [mode, setMode] = React5.useState("totp");
621
+ const [code, setCode] = React5.useState("");
622
+ const [trustDevice, setTrustDevice] = React5.useState(false);
688
623
  const switchMode = (next) => {
689
624
  setMode(next);
690
625
  setCode("");
@@ -708,11 +643,11 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
708
643
  const hint = mode === "totp" ? t("mfa.challenge.totpHint") : mode === "backup" ? t("mfa.challenge.backupHint") : t("mfa.challenge.otpHint");
709
644
  const label2 = mode === "totp" ? t("mfa.challenge.totpLabel") : mode === "backup" ? t("mfa.challenge.backupLabel") : t("mfa.challenge.otpLabel");
710
645
  return /* @__PURE__ */ jsxs7("form", { method: "post", className: "ba-fields", "data-testid": "mfa-challenge", onSubmit: submit, children: [
711
- /* @__PURE__ */ jsx8("h2", { className: "ba-title", children: t("mfa.challenge.title") }),
712
- /* @__PURE__ */ jsx8("p", { className: "ba-muted", children: hint }),
646
+ /* @__PURE__ */ jsx7("h2", { className: "ba-title", children: t("mfa.challenge.title") }),
647
+ /* @__PURE__ */ jsx7("p", { className: "ba-muted", children: hint }),
713
648
  /* @__PURE__ */ jsxs7("label", { className: "ba-label", children: [
714
649
  label2,
715
- /* @__PURE__ */ jsx8(
650
+ /* @__PURE__ */ jsx7(
716
651
  "input",
717
652
  {
718
653
  className: "ba-input",
@@ -730,24 +665,24 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
730
665
  ] }),
731
666
  mode !== "backup" && allowTrustDevice && /* @__PURE__ */ jsxs7("label", { className: "ba-consent ba-consent-centered", children: [
732
667
  /* @__PURE__ */ jsxs7("span", { className: "ba-checkbox-control", children: [
733
- /* @__PURE__ */ jsx8("input", { className: "ba-checkbox", type: "checkbox", checked: trustDevice, onChange: (e) => setTrustDevice(e.target.checked) }),
734
- /* @__PURE__ */ jsx8("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("span", { className: "ba-checkbox-check" }) })
668
+ /* @__PURE__ */ jsx7("input", { className: "ba-checkbox", type: "checkbox", checked: trustDevice, onChange: (e) => setTrustDevice(e.target.checked) }),
669
+ /* @__PURE__ */ jsx7("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("span", { className: "ba-checkbox-check" }) })
735
670
  ] }),
736
- /* @__PURE__ */ jsx8("span", { children: t("mfa.challenge.trustDevice", { days: 30 }) })
671
+ /* @__PURE__ */ jsx7("span", { children: t("mfa.challenge.trustDevice", { days: 30 }) })
737
672
  ] }),
738
- /* @__PURE__ */ jsx8(FormError, { children: error }),
739
- /* @__PURE__ */ jsx8("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx8(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.challenge.submit") }) }),
740
- mode !== "totp" && /* @__PURE__ */ jsx8("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("totp"), children: t("mfa.challenge.useTotp") }),
741
- mode !== "backup" && /* @__PURE__ */ jsx8("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("backup"), children: t("mfa.challenge.useBackup") }),
742
- /* @__PURE__ */ jsx8("button", { type: "button", className: "ba-link-button", disabled: pending, onClick: requestOtp, children: t(mode === "otp" ? "mfa.challenge.resendOtp" : "mfa.challenge.useEmailOtp") })
673
+ /* @__PURE__ */ jsx7(FormError, { children: error }),
674
+ /* @__PURE__ */ jsx7("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx7(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.challenge.submit") }) }),
675
+ mode !== "totp" && /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("totp"), children: t("mfa.challenge.useTotp") }),
676
+ mode !== "backup" && /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("backup"), children: t("mfa.challenge.useBackup") }),
677
+ /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", disabled: pending, onClick: requestOtp, children: t(mode === "otp" ? "mfa.challenge.resendOtp" : "mfa.challenge.useEmailOtp") })
743
678
  ] });
744
679
  }
745
680
 
746
681
  // src/components/mfa-enrollment-step.tsx
747
- import * as React9 from "react";
682
+ import * as React8 from "react";
748
683
 
749
684
  // src/components/MFAEnrollment.tsx
750
- import * as React8 from "react";
685
+ import * as React7 from "react";
751
686
 
752
687
  // src/components/mfa-enrollment.ts
753
688
  function shouldDiscardSetup(args) {
@@ -756,12 +691,12 @@ function shouldDiscardSetup(args) {
756
691
  }
757
692
 
758
693
  // src/components/QrCode.tsx
759
- import * as React7 from "react";
760
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
694
+ import * as React6 from "react";
695
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
761
696
  function QrCode({ value, size = 200 }) {
762
697
  const t = useT();
763
- const [path, setPath] = React7.useState(null);
764
- React7.useEffect(() => {
698
+ const [path, setPath] = React6.useState(null);
699
+ React6.useEffect(() => {
765
700
  let active = true;
766
701
  setPath(null);
767
702
  void import("./qr-ZNG4MAV5.js").then(
@@ -790,15 +725,15 @@ function QrCode({ value, size = 200 }) {
790
725
  shapeRendering: "crispEdges",
791
726
  className: "ba-qr",
792
727
  children: [
793
- /* @__PURE__ */ jsx9("rect", { width: dim, height: dim, fill: "#ffffff" }),
794
- /* @__PURE__ */ jsx9("g", { transform: `translate(${margin} ${margin})`, children: /* @__PURE__ */ jsx9("path", { d: path.d, fill: "#000000" }) })
728
+ /* @__PURE__ */ jsx8("rect", { width: dim, height: dim, fill: "#ffffff" }),
729
+ /* @__PURE__ */ jsx8("g", { transform: `translate(${margin} ${margin})`, children: /* @__PURE__ */ jsx8("path", { d: path.d, fill: "#000000" }) })
795
730
  ]
796
731
  }
797
732
  );
798
733
  }
799
734
 
800
735
  // src/components/MFAEnrollment.tsx
801
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
736
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
802
737
  function secretFromUri(uri) {
803
738
  try {
804
739
  return new URL(uri).searchParams.get("secret");
@@ -811,11 +746,11 @@ function MFAEnrollment({ onEnrolled, title }) {
811
746
  const { enable, verifyTotp } = useMFA();
812
747
  const { user, isLoaded } = useUser();
813
748
  const { pending, error, run } = useSubmitAction();
814
- const [password, setPassword] = React8.useState("");
815
- const [setup, setSetup] = React8.useState(null);
816
- const [code, setCode] = React8.useState("");
817
- const [done, setDone] = React8.useState(false);
818
- const setupOwnerId = React8.useRef(null);
749
+ const [password, setPassword] = React7.useState("");
750
+ const [setup, setSetup] = React7.useState(null);
751
+ const [code, setCode] = React7.useState("");
752
+ const [done, setDone] = React7.useState(false);
753
+ const setupOwnerId = React7.useRef(null);
819
754
  const userId = user?.id ?? null;
820
755
  const stale = shouldDiscardSetup({
821
756
  isLoaded,
@@ -824,7 +759,7 @@ function MFAEnrollment({ onEnrolled, title }) {
824
759
  currentUserId: userId
825
760
  });
826
761
  const activeSetup = stale ? null : setup;
827
- React8.useEffect(() => {
762
+ React7.useEffect(() => {
828
763
  if (stale) {
829
764
  setSetup(null);
830
765
  setPassword("");
@@ -833,18 +768,18 @@ function MFAEnrollment({ onEnrolled, title }) {
833
768
  }, [stale]);
834
769
  if (done) {
835
770
  return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-done", children: [
836
- /* @__PURE__ */ jsx10("h2", { className: "ba-title", children: t("mfa.enroll.doneTitle") }),
837
- /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfa.enroll.doneBody") })
771
+ /* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.doneTitle") }),
772
+ /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.doneBody") })
838
773
  ] });
839
774
  }
840
775
  if (!activeSetup) {
841
776
  if (!isLoaded) {
842
- return /* @__PURE__ */ jsx10("div", { className: "ba-fields", "data-testid": "mfa-enroll-loading", "aria-busy": "true", children: /* @__PURE__ */ jsx10("div", { className: "ba-skeleton" }) });
777
+ return /* @__PURE__ */ jsx9("div", { className: "ba-fields", "data-testid": "mfa-enroll-loading", "aria-busy": "true", children: /* @__PURE__ */ jsx9("div", { className: "ba-skeleton" }) });
843
778
  }
844
779
  if (user?.twoFactorEnabled) {
845
780
  return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-active", children: [
846
- /* @__PURE__ */ jsx10("h2", { className: "ba-title", children: t("mfa.enroll.activeTitle") }),
847
- /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfa.enroll.activeBody") })
781
+ /* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.activeTitle") }),
782
+ /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.activeBody") })
848
783
  ] });
849
784
  }
850
785
  return /* @__PURE__ */ jsxs9(
@@ -864,11 +799,11 @@ function MFAEnrollment({ onEnrolled, title }) {
864
799
  });
865
800
  },
866
801
  children: [
867
- title !== null && /* @__PURE__ */ jsx10("h2", { className: "ba-title", children: title ?? t("mfa.enroll.title") }),
868
- /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfa.enroll.passwordHint") }),
802
+ title !== null && /* @__PURE__ */ jsx9("h2", { className: "ba-title", children: title ?? t("mfa.enroll.title") }),
803
+ /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.passwordHint") }),
869
804
  /* @__PURE__ */ jsxs9("label", { className: "ba-label", children: [
870
805
  t("common.passwordLabel"),
871
- /* @__PURE__ */ jsx10(
806
+ /* @__PURE__ */ jsx9(
872
807
  "input",
873
808
  {
874
809
  className: "ba-input",
@@ -880,27 +815,27 @@ function MFAEnrollment({ onEnrolled, title }) {
880
815
  }
881
816
  )
882
817
  ] }),
883
- /* @__PURE__ */ jsx10(FormError, { children: error }),
884
- /* @__PURE__ */ jsx10("button", { className: "ba-button", type: "submit", disabled: pending || !password, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx10(Busy, { busy: pending, label: t("mfa.enroll.startPending"), children: t("common.continue") }) })
818
+ /* @__PURE__ */ jsx9(FormError, { children: error }),
819
+ /* @__PURE__ */ jsx9("button", { className: "ba-button", type: "submit", disabled: pending || !password, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx9(Busy, { busy: pending, label: t("mfa.enroll.startPending"), children: t("common.continue") }) })
885
820
  ]
886
821
  }
887
822
  );
888
823
  }
889
824
  const secret = secretFromUri(activeSetup.totpURI);
890
825
  return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-verify", children: [
891
- /* @__PURE__ */ jsx10("h2", { className: "ba-title", children: t("mfa.enroll.scanQrTitle") }),
892
- /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfa.enroll.scanQrHint") }),
893
- /* @__PURE__ */ jsx10(QrCode, { value: activeSetup.totpURI }),
894
- secret && /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: richMessage(t("mfa.enroll.manualKey"), {
895
- secret: /* @__PURE__ */ jsx10("code", { className: "ba-code", children: /* @__PURE__ */ jsx10(Bidi, { children: secret }) })
826
+ /* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.scanQrTitle") }),
827
+ /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.scanQrHint") }),
828
+ /* @__PURE__ */ jsx9(QrCode, { value: activeSetup.totpURI }),
829
+ secret && /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: richMessage(t("mfa.enroll.manualKey"), {
830
+ secret: /* @__PURE__ */ jsx9("code", { className: "ba-code", children: /* @__PURE__ */ jsx9(Bidi, { children: secret }) })
896
831
  }) }),
897
832
  /* @__PURE__ */ jsxs9("div", { children: [
898
833
  /* @__PURE__ */ jsxs9("p", { className: "ba-muted", children: [
899
- /* @__PURE__ */ jsx10("strong", { children: t("mfa.enroll.backupCodesWarningStrong") }),
834
+ /* @__PURE__ */ jsx9("strong", { children: t("mfa.enroll.backupCodesWarningStrong") }),
900
835
  " ",
901
836
  t("mfa.enroll.backupCodesWarningRest")
902
837
  ] }),
903
- /* @__PURE__ */ jsx10("ul", { className: "ba-backup-codes", "data-testid": "mfa-backup-codes", children: activeSetup.backupCodes.map((c) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10("code", { className: "ba-code", children: c }) }, c)) })
838
+ /* @__PURE__ */ jsx9("ul", { className: "ba-backup-codes", "data-testid": "mfa-backup-codes", children: activeSetup.backupCodes.map((c) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsx9("code", { className: "ba-code", children: c }) }, c)) })
904
839
  ] }),
905
840
  /* @__PURE__ */ jsxs9(
906
841
  "form",
@@ -922,7 +857,7 @@ function MFAEnrollment({ onEnrolled, title }) {
922
857
  children: [
923
858
  /* @__PURE__ */ jsxs9("label", { className: "ba-label", children: [
924
859
  t("mfa.enroll.codeLabel"),
925
- /* @__PURE__ */ jsx10(
860
+ /* @__PURE__ */ jsx9(
926
861
  "input",
927
862
  {
928
863
  className: "ba-input",
@@ -934,8 +869,8 @@ function MFAEnrollment({ onEnrolled, title }) {
934
869
  }
935
870
  )
936
871
  ] }),
937
- /* @__PURE__ */ jsx10(FormError, { children: error }),
938
- /* @__PURE__ */ jsx10("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx10(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.enroll.activateSubmit") }) })
872
+ /* @__PURE__ */ jsx9(FormError, { children: error }),
873
+ /* @__PURE__ */ jsx9("button", { className: "ba-button", type: "submit", disabled: pending || !code, "aria-busy": pending || void 0, children: /* @__PURE__ */ jsx9(Busy, { busy: pending, label: t("common.verifying"), children: t("mfa.enroll.activateSubmit") }) })
939
874
  ]
940
875
  }
941
876
  )
@@ -943,13 +878,13 @@ function MFAEnrollment({ onEnrolled, title }) {
943
878
  }
944
879
 
945
880
  // src/components/mfa-enrollment-step.tsx
946
- import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
881
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
947
882
  function useConfirmedMfaPending(onUnknown = "pending") {
948
883
  const client = useAuthClient();
949
884
  const { needsMfaEnrollment } = useUser();
950
885
  const { refetch } = useSession();
951
- const [confirmed, setConfirmed] = React9.useState(null);
952
- React9.useEffect(() => {
886
+ const [confirmed, setConfirmed] = React8.useState(null);
887
+ React8.useEffect(() => {
953
888
  if (!needsMfaEnrollment) {
954
889
  setConfirmed(null);
955
890
  return;
@@ -974,22 +909,22 @@ function MfaEnrollmentStep({ title }) {
974
909
  const t = useT();
975
910
  const { refetch } = useSession();
976
911
  return /* @__PURE__ */ jsxs10(Fragment2, { children: [
977
- /* @__PURE__ */ jsx11("h2", { className: "ba-title", children: title ?? t("mfaGate.title") }),
978
- /* @__PURE__ */ jsx11("p", { className: "ba-muted", children: t("mfaGate.body") }),
979
- /* @__PURE__ */ jsx11(MFAEnrollment, { onEnrolled: () => refetch({ query: { disableCookieCache: true } }) })
912
+ /* @__PURE__ */ jsx10("h2", { className: "ba-title", children: title ?? t("mfaGate.title") }),
913
+ /* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfaGate.body") }),
914
+ /* @__PURE__ */ jsx10(MFAEnrollment, { onEnrolled: () => refetch({ query: { disableCookieCache: true } }) })
980
915
  ] });
981
916
  }
982
917
 
983
918
  // src/components/AuthOwlBadge.tsx
984
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
919
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
985
920
  function OwlMark() {
986
921
  return /* @__PURE__ */ jsxs11("svg", { className: "ba-badge-mark", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: [
987
- /* @__PURE__ */ jsx12("circle", { cx: "12", cy: "12", r: "11", fill: "#F5B84C" }),
988
- /* @__PURE__ */ jsx12("circle", { cx: "8.4", cy: "10", r: "3", fill: "#fff" }),
989
- /* @__PURE__ */ jsx12("circle", { cx: "15.6", cy: "10", r: "3", fill: "#fff" }),
990
- /* @__PURE__ */ jsx12("circle", { cx: "8.4", cy: "10", r: "1.4", fill: "#1f1300" }),
991
- /* @__PURE__ */ jsx12("circle", { cx: "15.6", cy: "10", r: "1.4", fill: "#1f1300" }),
992
- /* @__PURE__ */ jsx12("path", { d: "M12 13.1l1.5 2.1h-3z", fill: "#1f1300" })
922
+ /* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "11", fill: "#F5B84C" }),
923
+ /* @__PURE__ */ jsx11("circle", { cx: "8.4", cy: "10", r: "3", fill: "#fff" }),
924
+ /* @__PURE__ */ jsx11("circle", { cx: "15.6", cy: "10", r: "3", fill: "#fff" }),
925
+ /* @__PURE__ */ jsx11("circle", { cx: "8.4", cy: "10", r: "1.4", fill: "#1f1300" }),
926
+ /* @__PURE__ */ jsx11("circle", { cx: "15.6", cy: "10", r: "1.4", fill: "#1f1300" }),
927
+ /* @__PURE__ */ jsx11("path", { d: "M12 13.1l1.5 2.1h-3z", fill: "#1f1300" })
993
928
  ] });
994
929
  }
995
930
  function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
@@ -1005,9 +940,9 @@ function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
1005
940
  rel: "noopener noreferrer",
1006
941
  "data-testid": "authowl-badge",
1007
942
  children: [
1008
- /* @__PURE__ */ jsx12(OwlMark, {}),
1009
- /* @__PURE__ */ jsx12("span", { children: richMessage(t("badge.securedBy"), {
1010
- brand: /* @__PURE__ */ jsx12("span", { className: "ba-badge-brand", children: "AuthOwl" })
943
+ /* @__PURE__ */ jsx11(OwlMark, {}),
944
+ /* @__PURE__ */ jsx11("span", { children: richMessage(t("badge.securedBy"), {
945
+ brand: /* @__PURE__ */ jsx11("span", { className: "ba-badge-brand", children: "AuthOwl" })
1011
946
  }) })
1012
947
  ]
1013
948
  }
@@ -1022,19 +957,19 @@ import {
1022
957
  } from "@authowl/core";
1023
958
 
1024
959
  // src/components/ConsentDocLinks.tsx
1025
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
960
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
1026
961
  function ConsentDocLinks({ termsUrl, privacyUrl }) {
1027
962
  const t = useT();
1028
963
  if (!termsUrl && !privacyUrl) return null;
1029
964
  return /* @__PURE__ */ jsxs12(Fragment3, { children: [
1030
- termsUrl && /* @__PURE__ */ jsx13("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.termsOfService") }),
965
+ termsUrl && /* @__PURE__ */ jsx12("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.termsOfService") }),
1031
966
  termsUrl && privacyUrl && t("consent.docJoiner"),
1032
- privacyUrl && /* @__PURE__ */ jsx13("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.privacyPolicy") })
967
+ privacyUrl && /* @__PURE__ */ jsx12("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.privacyPolicy") })
1033
968
  ] });
1034
969
  }
1035
970
 
1036
971
  // src/components/LegalConsentCheckbox.tsx
1037
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
972
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
1038
973
  function LegalConsentCheckbox({
1039
974
  legal,
1040
975
  accepted,
@@ -1045,7 +980,7 @@ function LegalConsentCheckbox({
1045
980
  if (!legal.required) return null;
1046
981
  return /* @__PURE__ */ jsxs13("label", { className: "ba-consent", "data-testid": testId, children: [
1047
982
  /* @__PURE__ */ jsxs13("span", { className: "ba-checkbox-control", children: [
1048
- /* @__PURE__ */ jsx14(
983
+ /* @__PURE__ */ jsx13(
1049
984
  "input",
1050
985
  {
1051
986
  className: "ba-checkbox",
@@ -1054,14 +989,69 @@ function LegalConsentCheckbox({
1054
989
  onChange: (event) => onAcceptedChange(event.target.checked)
1055
990
  }
1056
991
  ),
1057
- /* @__PURE__ */ jsx14("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx14("span", { className: "ba-checkbox-check" }) })
992
+ /* @__PURE__ */ jsx13("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx13("span", { className: "ba-checkbox-check" }) })
1058
993
  ] }),
1059
- /* @__PURE__ */ jsx14("span", { children: richMessage(t("signUp.consentLabel"), {
1060
- links: /* @__PURE__ */ jsx14(ConsentDocLinks, { termsUrl: legal.termsUrl, privacyUrl: legal.privacyUrl })
994
+ /* @__PURE__ */ jsx13("span", { children: richMessage(t("signUp.consentLabel"), {
995
+ links: /* @__PURE__ */ jsx13(ConsentDocLinks, { termsUrl: legal.termsUrl, privacyUrl: legal.privacyUrl })
1061
996
  }) })
1062
997
  ] });
1063
998
  }
1064
999
 
1000
+ // src/components/Turnstile.tsx
1001
+ import * as React9 from "react";
1002
+ import { jsx as jsx14 } from "react/jsx-runtime";
1003
+ var LOCAL_TURNSTILE_TEST_TOKEN = "XXXX.DUMMY.TOKEN.XXXX";
1004
+ async function loadTurnstile() {
1005
+ const [{ loadCaptcha }, { CAPTCHA_ADAPTERS }] = await Promise.all([
1006
+ import("./captcha-loader-VNULARCZ.js"),
1007
+ import("./captcha-providers-STYFL25P.js")
1008
+ ]);
1009
+ const adapter = CAPTCHA_ADAPTERS.turnstile;
1010
+ return { api: await loadCaptcha(adapter), adapter };
1011
+ }
1012
+ function Turnstile({
1013
+ siteKey,
1014
+ theme,
1015
+ onToken,
1016
+ onUnavailable
1017
+ }) {
1018
+ const container = React9.useRef(null);
1019
+ const callbacks = React9.useRef({ onToken, onUnavailable });
1020
+ callbacks.current = { onToken, onUnavailable };
1021
+ React9.useEffect(() => {
1022
+ callbacks.current.onToken(null);
1023
+ if (!siteKey) {
1024
+ callbacks.current.onToken(LOCAL_TURNSTILE_TEST_TOKEN);
1025
+ return;
1026
+ }
1027
+ let active = true;
1028
+ let widget = null;
1029
+ void loadTurnstile().then(({ api, adapter }) => {
1030
+ if (!active || !container.current) return;
1031
+ const id = api.render(container.current, {
1032
+ sitekey: siteKey,
1033
+ theme: theme === "system" ? "auto" : theme,
1034
+ size: "flexible",
1035
+ callback: (token) => callbacks.current.onToken(token),
1036
+ "expired-callback": () => callbacks.current.onToken(null),
1037
+ "error-callback": () => {
1038
+ callbacks.current.onToken(null);
1039
+ callbacks.current.onUnavailable();
1040
+ }
1041
+ });
1042
+ widget = { api, adapter, id };
1043
+ }).catch(() => {
1044
+ if (active) callbacks.current.onUnavailable();
1045
+ });
1046
+ return () => {
1047
+ active = false;
1048
+ if (widget) widget.adapter.teardown(widget.api, widget.id);
1049
+ };
1050
+ }, [siteKey, theme]);
1051
+ if (!siteKey) return null;
1052
+ return /* @__PURE__ */ jsx14("div", { className: "ba-turnstile", ref: container, "data-testid": "phoneotp-turnstile" });
1053
+ }
1054
+
1065
1055
  // src/components/PhoneOTP.tsx
1066
1056
  import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1067
1057
  function PhoneOTP({
@@ -3449,9 +3439,9 @@ function formatSessionTime(date, locale) {
3449
3439
 
3450
3440
  // src/components/user-profile/use-account-resource.ts
3451
3441
  import * as React32 from "react";
3452
- function useAccountResource(loader2, failure) {
3453
- const loaderRef = React32.useRef(loader2);
3454
- loaderRef.current = loader2;
3442
+ function useAccountResource(loader, failure) {
3443
+ const loaderRef = React32.useRef(loader);
3444
+ loaderRef.current = loader;
3455
3445
  const failureRef = React32.useRef(failure);
3456
3446
  failureRef.current = failure;
3457
3447
  const toServerError = useServerError();