@authowl/react 0.21.2 → 0.23.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/{TeamsSection-ZQDNMWV3.js → TeamsSection-NV64O5I4.js} +1 -1
- package/dist/captcha-loader-VNULARCZ.js +68 -0
- package/dist/captcha-providers-STYFL25P.js +71 -0
- package/dist/{chunk-PUJV7S6G.js → chunk-E5ZOQHXY.js} +97 -51
- package/dist/index.cjs +730 -498
- package/dist/index.d.cts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +253 -227
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
useConsent,
|
|
21
21
|
useEmailVerification,
|
|
22
22
|
useInvitationRecipientHint,
|
|
23
|
+
useLastUsedSignInMethod,
|
|
23
24
|
useLocale,
|
|
24
25
|
useMFA,
|
|
25
26
|
useOrganization,
|
|
@@ -31,13 +32,14 @@ import {
|
|
|
31
32
|
useServerError,
|
|
32
33
|
useSession,
|
|
33
34
|
useSignIn,
|
|
35
|
+
useSignInMethodRecorder,
|
|
34
36
|
useSignOut,
|
|
35
37
|
useSignUp,
|
|
36
38
|
useSubmitAction,
|
|
37
39
|
useT,
|
|
38
40
|
useUser,
|
|
39
41
|
useWaitlist
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-E5ZOQHXY.js";
|
|
41
43
|
|
|
42
44
|
// src/project-capabilities.ts
|
|
43
45
|
import {
|
|
@@ -276,6 +278,7 @@ function SocialButtons({ providers, callbackURL }) {
|
|
|
276
278
|
const t = useT();
|
|
277
279
|
const toServerError = useServerError();
|
|
278
280
|
const { signInSocial } = useSignIn();
|
|
281
|
+
const recordSignInMethod = useSignInMethodRecorder();
|
|
279
282
|
const [pending, setPending] = React2.useState(null);
|
|
280
283
|
const [error, setError] = React2.useState(null);
|
|
281
284
|
React2.useEffect(() => {
|
|
@@ -308,6 +311,8 @@ function SocialButtons({ providers, callbackURL }) {
|
|
|
308
311
|
if (res?.error) {
|
|
309
312
|
setError(toServerError(res.error, t("social.error.startFailed")));
|
|
310
313
|
setPending(null);
|
|
314
|
+
} else {
|
|
315
|
+
recordSignInMethod(`social:${provider}`, true);
|
|
311
316
|
}
|
|
312
317
|
} catch {
|
|
313
318
|
setError(t("social.error.startFailed"));
|
|
@@ -356,104 +361,19 @@ function PasskeyButton({ redirectTo, onSignedIn }) {
|
|
|
356
361
|
}
|
|
357
362
|
|
|
358
363
|
// src/components/ForgotPassword.tsx
|
|
359
|
-
import * as React5 from "react";
|
|
360
|
-
|
|
361
|
-
// src/components/AuthChallenge.tsx
|
|
362
364
|
import * as React4 from "react";
|
|
363
365
|
|
|
364
|
-
// src/components/
|
|
366
|
+
// src/components/AuthChallenge.tsx
|
|
365
367
|
import * as React3 from "react";
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
var
|
|
369
|
-
|
|
370
|
-
|
|
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" });
|
|
368
|
+
|
|
369
|
+
// src/components/captcha-provider-ids.ts
|
|
370
|
+
var CAPTCHA_PROVIDER_IDS = ["turnstile", "hcaptcha", "recaptcha-v2"];
|
|
371
|
+
function isSupportedCaptchaProvider(provider) {
|
|
372
|
+
return CAPTCHA_PROVIDER_IDS.includes(provider);
|
|
453
373
|
}
|
|
454
374
|
|
|
455
375
|
// src/components/AuthChallenge.tsx
|
|
456
|
-
import { jsx as
|
|
376
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
457
377
|
var AUTH_CHALLENGE_ACTIONS = {
|
|
458
378
|
signUp: "auth_signup",
|
|
459
379
|
signIn: "auth_signin",
|
|
@@ -468,38 +388,53 @@ var challengeError = {
|
|
|
468
388
|
code: "BOT_CHALLENGE_FAILED",
|
|
469
389
|
message: "Human verification failed."
|
|
470
390
|
};
|
|
471
|
-
function
|
|
391
|
+
function captchaTheme(root, fallback) {
|
|
472
392
|
const rendered = root?.dataset.authowlTheme;
|
|
473
393
|
if (rendered === "light" || rendered === "dark") return rendered;
|
|
474
394
|
if (fallback === "light" || fallback === "dark") return fallback;
|
|
475
395
|
return "auto";
|
|
476
396
|
}
|
|
477
397
|
function useAuthChallenge() {
|
|
478
|
-
const { config, isLoading } = usePublicConfig();
|
|
398
|
+
const { config, isLoading, retry } = usePublicConfig();
|
|
479
399
|
const t = useT();
|
|
480
|
-
const container =
|
|
481
|
-
const active =
|
|
482
|
-
const [status, setStatus] =
|
|
483
|
-
const
|
|
484
|
-
const
|
|
400
|
+
const container = React3.useRef(null);
|
|
401
|
+
const active = React3.useRef(null);
|
|
402
|
+
const [status, setStatus] = React3.useState("idle");
|
|
403
|
+
const captcha = config?.captcha ?? null;
|
|
404
|
+
const providerSupported = captcha ? isSupportedCaptchaProvider(captcha.provider) : false;
|
|
405
|
+
const unavailableProvider = captcha && !providerSupported ? captcha.provider : null;
|
|
406
|
+
const refetched = React3.useRef(null);
|
|
407
|
+
const refetchIfConfigMayBeStale = React3.useCallback(() => {
|
|
408
|
+
if (!config || refetched.current === config) return;
|
|
409
|
+
refetched.current = config;
|
|
410
|
+
retry();
|
|
411
|
+
}, [config, retry]);
|
|
412
|
+
const removeActive = React3.useCallback((reason) => {
|
|
485
413
|
const current = active.current;
|
|
486
414
|
active.current = null;
|
|
487
415
|
if (!current) return;
|
|
488
|
-
current.
|
|
416
|
+
current.adapter.teardown(current.api, current.id);
|
|
489
417
|
if (reason) current.reject(reason);
|
|
490
418
|
}, []);
|
|
491
|
-
|
|
492
|
-
() => () => removeActive(new Error("
|
|
419
|
+
React3.useEffect(
|
|
420
|
+
() => () => removeActive(new Error("Captcha challenge was cancelled")),
|
|
493
421
|
[removeActive]
|
|
494
422
|
);
|
|
495
|
-
const tokenFor =
|
|
423
|
+
const tokenFor = React3.useCallback(
|
|
496
424
|
async (action) => {
|
|
497
|
-
if (!
|
|
425
|
+
if (!captcha) return null;
|
|
426
|
+
if (!providerSupported) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
|
|
498
427
|
setStatus("checking");
|
|
499
|
-
removeActive(new Error("
|
|
428
|
+
removeActive(new Error("Captcha challenge was replaced"));
|
|
500
429
|
try {
|
|
501
|
-
const
|
|
502
|
-
|
|
430
|
+
const [{ loadCaptcha }, { captchaAdapterFor }] = await Promise.all([
|
|
431
|
+
import("./captcha-loader-VNULARCZ.js"),
|
|
432
|
+
import("./captcha-providers-STYFL25P.js")
|
|
433
|
+
]);
|
|
434
|
+
const adapter = captchaAdapterFor(captcha.provider);
|
|
435
|
+
if (!adapter) throw new Error(`Unsupported captcha provider: ${captcha.provider}`);
|
|
436
|
+
const api = await loadCaptcha(adapter);
|
|
437
|
+
if (!container.current) throw new Error("Captcha container unavailable");
|
|
503
438
|
return await new Promise((resolve, reject) => {
|
|
504
439
|
let settled = false;
|
|
505
440
|
const finish = (outcome) => {
|
|
@@ -507,27 +442,29 @@ function useAuthChallenge() {
|
|
|
507
442
|
settled = true;
|
|
508
443
|
const current = active.current;
|
|
509
444
|
active.current = null;
|
|
510
|
-
if (current) current.
|
|
445
|
+
if (current) current.adapter.teardown(current.api, current.id);
|
|
511
446
|
if ("token" in outcome) resolve(outcome.token);
|
|
512
447
|
else reject(outcome.error);
|
|
513
448
|
};
|
|
514
|
-
const fail = () => finish({ error: new Error("
|
|
449
|
+
const fail = () => finish({ error: new Error("Captcha challenge failed") });
|
|
515
450
|
const root = container.current.closest(".authowl-root");
|
|
451
|
+
const optionalFailureCallbacks = captcha.provider === "turnstile" ? {
|
|
452
|
+
"timeout-callback": fail,
|
|
453
|
+
"unsupported-callback": fail
|
|
454
|
+
} : {};
|
|
516
455
|
const id = api.render(container.current, {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
language: root?.dataset.authowlLocale ?? config?.locale,
|
|
456
|
+
...adapter.invisibleRenderOptions({
|
|
457
|
+
siteKey: captcha.siteKey,
|
|
458
|
+
theme: captchaTheme(root, config?.branding?.theme),
|
|
459
|
+
action,
|
|
460
|
+
language: root?.dataset.authowlLocale ?? config?.locale
|
|
461
|
+
}),
|
|
524
462
|
callback: (token) => finish({ token }),
|
|
525
463
|
"expired-callback": fail,
|
|
526
464
|
"error-callback": fail,
|
|
527
|
-
|
|
528
|
-
"unsupported-callback": fail
|
|
465
|
+
...optionalFailureCallbacks
|
|
529
466
|
});
|
|
530
|
-
active.current = { api, id, reject };
|
|
467
|
+
active.current = { adapter, api, id, reject };
|
|
531
468
|
try {
|
|
532
469
|
api.execute(id);
|
|
533
470
|
} catch {
|
|
@@ -538,9 +475,9 @@ function useAuthChallenge() {
|
|
|
538
475
|
setStatus("idle");
|
|
539
476
|
}
|
|
540
477
|
},
|
|
541
|
-
[config?.branding?.theme, config?.locale,
|
|
478
|
+
[captcha, config?.branding?.theme, config?.locale, providerSupported, removeActive]
|
|
542
479
|
);
|
|
543
|
-
const run =
|
|
480
|
+
const run = React3.useCallback(
|
|
544
481
|
async (action, request) => {
|
|
545
482
|
if (isLoading) {
|
|
546
483
|
return {
|
|
@@ -550,37 +487,40 @@ function useAuthChallenge() {
|
|
|
550
487
|
}
|
|
551
488
|
try {
|
|
552
489
|
const token = await tokenFor(action);
|
|
553
|
-
|
|
490
|
+
const result = await request(token ? { authChallengeToken: token } : void 0);
|
|
491
|
+
if (result?.error?.code === challengeError.code) refetchIfConfigMayBeStale();
|
|
492
|
+
return result;
|
|
554
493
|
} catch {
|
|
555
494
|
setStatus("failed");
|
|
495
|
+
refetchIfConfigMayBeStale();
|
|
556
496
|
return {
|
|
557
497
|
data: null,
|
|
558
498
|
error: challengeError
|
|
559
499
|
};
|
|
560
500
|
}
|
|
561
501
|
},
|
|
562
|
-
[isLoading, tokenFor]
|
|
502
|
+
[isLoading, refetchIfConfigMayBeStale, tokenFor]
|
|
563
503
|
);
|
|
564
|
-
const control =
|
|
565
|
-
/* @__PURE__ */
|
|
566
|
-
/* @__PURE__ */
|
|
504
|
+
const control = captcha ? /* @__PURE__ */ jsxs4("div", { className: "ba-auth-challenge", "data-testid": "auth-challenge", children: [
|
|
505
|
+
providerSupported ? /* @__PURE__ */ jsx4("div", { className: "ba-turnstile", ref: container }) : null,
|
|
506
|
+
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
507
|
] }) : null;
|
|
568
508
|
return { run, control, configPending: isLoading };
|
|
569
509
|
}
|
|
570
510
|
|
|
571
511
|
// src/components/ForgotPassword.tsx
|
|
572
|
-
import { jsx as
|
|
512
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
573
513
|
function ForgotPassword({ resetPasswordUrl, onBack }) {
|
|
574
514
|
const t = useT();
|
|
575
515
|
const { requestPasswordReset } = usePasswordReset();
|
|
576
516
|
const { pending, error, run } = useSubmitAction();
|
|
577
517
|
const authChallenge = useAuthChallenge();
|
|
578
|
-
const [email, setEmail] =
|
|
579
|
-
const [sent, setSent] =
|
|
518
|
+
const [email, setEmail] = React4.useState("");
|
|
519
|
+
const [sent, setSent] = React4.useState(false);
|
|
580
520
|
if (sent) {
|
|
581
521
|
return /* @__PURE__ */ jsxs5("div", { className: "ba-fields", "data-testid": "forgot-sent", children: [
|
|
582
|
-
/* @__PURE__ */
|
|
583
|
-
onBack && /* @__PURE__ */
|
|
522
|
+
/* @__PURE__ */ jsx5("p", { className: "ba-muted", children: richMessage(t("forgotPassword.sent"), { email: /* @__PURE__ */ jsx5(Bidi, { children: email }) }) }),
|
|
523
|
+
onBack && /* @__PURE__ */ jsx5("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
|
|
584
524
|
] });
|
|
585
525
|
}
|
|
586
526
|
return /* @__PURE__ */ jsxs5(
|
|
@@ -599,7 +539,7 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
|
|
|
599
539
|
children: [
|
|
600
540
|
/* @__PURE__ */ jsxs5("label", { className: "ba-label", children: [
|
|
601
541
|
t("common.emailLabel"),
|
|
602
|
-
/* @__PURE__ */
|
|
542
|
+
/* @__PURE__ */ jsx5(
|
|
603
543
|
"input",
|
|
604
544
|
{
|
|
605
545
|
className: "ba-input",
|
|
@@ -612,25 +552,25 @@ function ForgotPassword({ resetPasswordUrl, onBack }) {
|
|
|
612
552
|
)
|
|
613
553
|
] }),
|
|
614
554
|
authChallenge.control,
|
|
615
|
-
/* @__PURE__ */
|
|
616
|
-
/* @__PURE__ */
|
|
555
|
+
/* @__PURE__ */ jsx5(FormError, { children: error }),
|
|
556
|
+
/* @__PURE__ */ jsx5(
|
|
617
557
|
"button",
|
|
618
558
|
{
|
|
619
559
|
className: "ba-button",
|
|
620
560
|
type: "submit",
|
|
621
561
|
disabled: pending || authChallenge.configPending,
|
|
622
562
|
"aria-busy": pending || void 0,
|
|
623
|
-
children: /* @__PURE__ */
|
|
563
|
+
children: /* @__PURE__ */ jsx5(Busy, { busy: pending, label: t("common.sending"), children: t("forgotPassword.submit") })
|
|
624
564
|
}
|
|
625
565
|
),
|
|
626
|
-
onBack && /* @__PURE__ */
|
|
566
|
+
onBack && /* @__PURE__ */ jsx5("button", { type: "button", className: "ba-link-button", onClick: onBack, children: t("forgotPassword.backToSignIn") })
|
|
627
567
|
]
|
|
628
568
|
}
|
|
629
569
|
);
|
|
630
570
|
}
|
|
631
571
|
|
|
632
572
|
// src/components/OtpCodeForm.tsx
|
|
633
|
-
import { jsx as
|
|
573
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
634
574
|
function OtpCodeForm({
|
|
635
575
|
email,
|
|
636
576
|
code,
|
|
@@ -653,8 +593,8 @@ function OtpCodeForm({
|
|
|
653
593
|
},
|
|
654
594
|
children: [
|
|
655
595
|
/* @__PURE__ */ jsxs6("label", { className: "ba-label", children: [
|
|
656
|
-
richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */
|
|
657
|
-
/* @__PURE__ */
|
|
596
|
+
richMessage(t("emailOtp.codeLabel"), { email: /* @__PURE__ */ jsx6(Bidi, { children: email }) }),
|
|
597
|
+
/* @__PURE__ */ jsx6(
|
|
658
598
|
"input",
|
|
659
599
|
{
|
|
660
600
|
className: "ba-input",
|
|
@@ -667,24 +607,24 @@ function OtpCodeForm({
|
|
|
667
607
|
}
|
|
668
608
|
)
|
|
669
609
|
] }),
|
|
670
|
-
/* @__PURE__ */
|
|
671
|
-
/* @__PURE__ */
|
|
672
|
-
/* @__PURE__ */
|
|
610
|
+
/* @__PURE__ */ jsx6(FormError, { children: error }),
|
|
611
|
+
/* @__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") }) }),
|
|
612
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "ba-link-button", onClick: onChangeEmail, children: t("emailOtp.changeEmail") })
|
|
673
613
|
]
|
|
674
614
|
}
|
|
675
615
|
);
|
|
676
616
|
}
|
|
677
617
|
|
|
678
618
|
// src/components/MFAChallenge.tsx
|
|
679
|
-
import * as
|
|
680
|
-
import { jsx as
|
|
619
|
+
import * as React5 from "react";
|
|
620
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
681
621
|
function MFAChallenge({ onVerified, allowTrustDevice = true }) {
|
|
682
622
|
const t = useT();
|
|
683
623
|
const { verifyTotp, verifyBackupCode, sendOtp, verifyOtp } = useMFA();
|
|
684
624
|
const { pending, error, setError, run } = useSubmitAction();
|
|
685
|
-
const [mode, setMode] =
|
|
686
|
-
const [code, setCode] =
|
|
687
|
-
const [trustDevice, setTrustDevice] =
|
|
625
|
+
const [mode, setMode] = React5.useState("totp");
|
|
626
|
+
const [code, setCode] = React5.useState("");
|
|
627
|
+
const [trustDevice, setTrustDevice] = React5.useState(false);
|
|
688
628
|
const switchMode = (next) => {
|
|
689
629
|
setMode(next);
|
|
690
630
|
setCode("");
|
|
@@ -708,11 +648,11 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
|
|
|
708
648
|
const hint = mode === "totp" ? t("mfa.challenge.totpHint") : mode === "backup" ? t("mfa.challenge.backupHint") : t("mfa.challenge.otpHint");
|
|
709
649
|
const label2 = mode === "totp" ? t("mfa.challenge.totpLabel") : mode === "backup" ? t("mfa.challenge.backupLabel") : t("mfa.challenge.otpLabel");
|
|
710
650
|
return /* @__PURE__ */ jsxs7("form", { method: "post", className: "ba-fields", "data-testid": "mfa-challenge", onSubmit: submit, children: [
|
|
711
|
-
/* @__PURE__ */
|
|
712
|
-
/* @__PURE__ */
|
|
651
|
+
/* @__PURE__ */ jsx7("h2", { className: "ba-title", children: t("mfa.challenge.title") }),
|
|
652
|
+
/* @__PURE__ */ jsx7("p", { className: "ba-muted", children: hint }),
|
|
713
653
|
/* @__PURE__ */ jsxs7("label", { className: "ba-label", children: [
|
|
714
654
|
label2,
|
|
715
|
-
/* @__PURE__ */
|
|
655
|
+
/* @__PURE__ */ jsx7(
|
|
716
656
|
"input",
|
|
717
657
|
{
|
|
718
658
|
className: "ba-input",
|
|
@@ -730,24 +670,24 @@ function MFAChallenge({ onVerified, allowTrustDevice = true }) {
|
|
|
730
670
|
] }),
|
|
731
671
|
mode !== "backup" && allowTrustDevice && /* @__PURE__ */ jsxs7("label", { className: "ba-consent ba-consent-centered", children: [
|
|
732
672
|
/* @__PURE__ */ jsxs7("span", { className: "ba-checkbox-control", children: [
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
673
|
+
/* @__PURE__ */ jsx7("input", { className: "ba-checkbox", type: "checkbox", checked: trustDevice, onChange: (e) => setTrustDevice(e.target.checked) }),
|
|
674
|
+
/* @__PURE__ */ jsx7("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("span", { className: "ba-checkbox-check" }) })
|
|
735
675
|
] }),
|
|
736
|
-
/* @__PURE__ */
|
|
676
|
+
/* @__PURE__ */ jsx7("span", { children: t("mfa.challenge.trustDevice", { days: 30 }) })
|
|
737
677
|
] }),
|
|
738
|
-
/* @__PURE__ */
|
|
739
|
-
/* @__PURE__ */
|
|
740
|
-
mode !== "totp" && /* @__PURE__ */
|
|
741
|
-
mode !== "backup" && /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
678
|
+
/* @__PURE__ */ jsx7(FormError, { children: error }),
|
|
679
|
+
/* @__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") }) }),
|
|
680
|
+
mode !== "totp" && /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("totp"), children: t("mfa.challenge.useTotp") }),
|
|
681
|
+
mode !== "backup" && /* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", onClick: () => switchMode("backup"), children: t("mfa.challenge.useBackup") }),
|
|
682
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "ba-link-button", disabled: pending, onClick: requestOtp, children: t(mode === "otp" ? "mfa.challenge.resendOtp" : "mfa.challenge.useEmailOtp") })
|
|
743
683
|
] });
|
|
744
684
|
}
|
|
745
685
|
|
|
746
686
|
// src/components/mfa-enrollment-step.tsx
|
|
747
|
-
import * as
|
|
687
|
+
import * as React8 from "react";
|
|
748
688
|
|
|
749
689
|
// src/components/MFAEnrollment.tsx
|
|
750
|
-
import * as
|
|
690
|
+
import * as React7 from "react";
|
|
751
691
|
|
|
752
692
|
// src/components/mfa-enrollment.ts
|
|
753
693
|
function shouldDiscardSetup(args) {
|
|
@@ -756,12 +696,12 @@ function shouldDiscardSetup(args) {
|
|
|
756
696
|
}
|
|
757
697
|
|
|
758
698
|
// src/components/QrCode.tsx
|
|
759
|
-
import * as
|
|
760
|
-
import { jsx as
|
|
699
|
+
import * as React6 from "react";
|
|
700
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
761
701
|
function QrCode({ value, size = 200 }) {
|
|
762
702
|
const t = useT();
|
|
763
|
-
const [path, setPath] =
|
|
764
|
-
|
|
703
|
+
const [path, setPath] = React6.useState(null);
|
|
704
|
+
React6.useEffect(() => {
|
|
765
705
|
let active = true;
|
|
766
706
|
setPath(null);
|
|
767
707
|
void import("./qr-ZNG4MAV5.js").then(
|
|
@@ -790,15 +730,15 @@ function QrCode({ value, size = 200 }) {
|
|
|
790
730
|
shapeRendering: "crispEdges",
|
|
791
731
|
className: "ba-qr",
|
|
792
732
|
children: [
|
|
793
|
-
/* @__PURE__ */
|
|
794
|
-
/* @__PURE__ */
|
|
733
|
+
/* @__PURE__ */ jsx8("rect", { width: dim, height: dim, fill: "#ffffff" }),
|
|
734
|
+
/* @__PURE__ */ jsx8("g", { transform: `translate(${margin} ${margin})`, children: /* @__PURE__ */ jsx8("path", { d: path.d, fill: "#000000" }) })
|
|
795
735
|
]
|
|
796
736
|
}
|
|
797
737
|
);
|
|
798
738
|
}
|
|
799
739
|
|
|
800
740
|
// src/components/MFAEnrollment.tsx
|
|
801
|
-
import { jsx as
|
|
741
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
802
742
|
function secretFromUri(uri) {
|
|
803
743
|
try {
|
|
804
744
|
return new URL(uri).searchParams.get("secret");
|
|
@@ -811,11 +751,11 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
811
751
|
const { enable, verifyTotp } = useMFA();
|
|
812
752
|
const { user, isLoaded } = useUser();
|
|
813
753
|
const { pending, error, run } = useSubmitAction();
|
|
814
|
-
const [password, setPassword] =
|
|
815
|
-
const [setup, setSetup] =
|
|
816
|
-
const [code, setCode] =
|
|
817
|
-
const [done, setDone] =
|
|
818
|
-
const setupOwnerId =
|
|
754
|
+
const [password, setPassword] = React7.useState("");
|
|
755
|
+
const [setup, setSetup] = React7.useState(null);
|
|
756
|
+
const [code, setCode] = React7.useState("");
|
|
757
|
+
const [done, setDone] = React7.useState(false);
|
|
758
|
+
const setupOwnerId = React7.useRef(null);
|
|
819
759
|
const userId = user?.id ?? null;
|
|
820
760
|
const stale = shouldDiscardSetup({
|
|
821
761
|
isLoaded,
|
|
@@ -824,7 +764,7 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
824
764
|
currentUserId: userId
|
|
825
765
|
});
|
|
826
766
|
const activeSetup = stale ? null : setup;
|
|
827
|
-
|
|
767
|
+
React7.useEffect(() => {
|
|
828
768
|
if (stale) {
|
|
829
769
|
setSetup(null);
|
|
830
770
|
setPassword("");
|
|
@@ -833,18 +773,18 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
833
773
|
}, [stale]);
|
|
834
774
|
if (done) {
|
|
835
775
|
return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-done", children: [
|
|
836
|
-
/* @__PURE__ */
|
|
837
|
-
/* @__PURE__ */
|
|
776
|
+
/* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.doneTitle") }),
|
|
777
|
+
/* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.doneBody") })
|
|
838
778
|
] });
|
|
839
779
|
}
|
|
840
780
|
if (!activeSetup) {
|
|
841
781
|
if (!isLoaded) {
|
|
842
|
-
return /* @__PURE__ */
|
|
782
|
+
return /* @__PURE__ */ jsx9("div", { className: "ba-fields", "data-testid": "mfa-enroll-loading", "aria-busy": "true", children: /* @__PURE__ */ jsx9("div", { className: "ba-skeleton" }) });
|
|
843
783
|
}
|
|
844
784
|
if (user?.twoFactorEnabled) {
|
|
845
785
|
return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-active", children: [
|
|
846
|
-
/* @__PURE__ */
|
|
847
|
-
/* @__PURE__ */
|
|
786
|
+
/* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.activeTitle") }),
|
|
787
|
+
/* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.activeBody") })
|
|
848
788
|
] });
|
|
849
789
|
}
|
|
850
790
|
return /* @__PURE__ */ jsxs9(
|
|
@@ -864,11 +804,11 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
864
804
|
});
|
|
865
805
|
},
|
|
866
806
|
children: [
|
|
867
|
-
title !== null && /* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
807
|
+
title !== null && /* @__PURE__ */ jsx9("h2", { className: "ba-title", children: title ?? t("mfa.enroll.title") }),
|
|
808
|
+
/* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.passwordHint") }),
|
|
869
809
|
/* @__PURE__ */ jsxs9("label", { className: "ba-label", children: [
|
|
870
810
|
t("common.passwordLabel"),
|
|
871
|
-
/* @__PURE__ */
|
|
811
|
+
/* @__PURE__ */ jsx9(
|
|
872
812
|
"input",
|
|
873
813
|
{
|
|
874
814
|
className: "ba-input",
|
|
@@ -880,27 +820,27 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
880
820
|
}
|
|
881
821
|
)
|
|
882
822
|
] }),
|
|
883
|
-
/* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */
|
|
823
|
+
/* @__PURE__ */ jsx9(FormError, { children: error }),
|
|
824
|
+
/* @__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
825
|
]
|
|
886
826
|
}
|
|
887
827
|
);
|
|
888
828
|
}
|
|
889
829
|
const secret = secretFromUri(activeSetup.totpURI);
|
|
890
830
|
return /* @__PURE__ */ jsxs9("div", { className: "ba-fields", "data-testid": "mfa-enroll-verify", children: [
|
|
891
|
-
/* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
/* @__PURE__ */
|
|
894
|
-
secret && /* @__PURE__ */
|
|
895
|
-
secret: /* @__PURE__ */
|
|
831
|
+
/* @__PURE__ */ jsx9("h2", { className: "ba-title", children: t("mfa.enroll.scanQrTitle") }),
|
|
832
|
+
/* @__PURE__ */ jsx9("p", { className: "ba-muted", children: t("mfa.enroll.scanQrHint") }),
|
|
833
|
+
/* @__PURE__ */ jsx9(QrCode, { value: activeSetup.totpURI }),
|
|
834
|
+
secret && /* @__PURE__ */ jsx9("p", { className: "ba-muted", children: richMessage(t("mfa.enroll.manualKey"), {
|
|
835
|
+
secret: /* @__PURE__ */ jsx9("code", { className: "ba-code", children: /* @__PURE__ */ jsx9(Bidi, { children: secret }) })
|
|
896
836
|
}) }),
|
|
897
837
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
898
838
|
/* @__PURE__ */ jsxs9("p", { className: "ba-muted", children: [
|
|
899
|
-
/* @__PURE__ */
|
|
839
|
+
/* @__PURE__ */ jsx9("strong", { children: t("mfa.enroll.backupCodesWarningStrong") }),
|
|
900
840
|
" ",
|
|
901
841
|
t("mfa.enroll.backupCodesWarningRest")
|
|
902
842
|
] }),
|
|
903
|
-
/* @__PURE__ */
|
|
843
|
+
/* @__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
844
|
] }),
|
|
905
845
|
/* @__PURE__ */ jsxs9(
|
|
906
846
|
"form",
|
|
@@ -922,7 +862,7 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
922
862
|
children: [
|
|
923
863
|
/* @__PURE__ */ jsxs9("label", { className: "ba-label", children: [
|
|
924
864
|
t("mfa.enroll.codeLabel"),
|
|
925
|
-
/* @__PURE__ */
|
|
865
|
+
/* @__PURE__ */ jsx9(
|
|
926
866
|
"input",
|
|
927
867
|
{
|
|
928
868
|
className: "ba-input",
|
|
@@ -934,8 +874,8 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
934
874
|
}
|
|
935
875
|
)
|
|
936
876
|
] }),
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
877
|
+
/* @__PURE__ */ jsx9(FormError, { children: error }),
|
|
878
|
+
/* @__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
879
|
]
|
|
940
880
|
}
|
|
941
881
|
)
|
|
@@ -943,13 +883,13 @@ function MFAEnrollment({ onEnrolled, title }) {
|
|
|
943
883
|
}
|
|
944
884
|
|
|
945
885
|
// src/components/mfa-enrollment-step.tsx
|
|
946
|
-
import { Fragment as Fragment2, jsx as
|
|
886
|
+
import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
947
887
|
function useConfirmedMfaPending(onUnknown = "pending") {
|
|
948
888
|
const client = useAuthClient();
|
|
949
889
|
const { needsMfaEnrollment } = useUser();
|
|
950
890
|
const { refetch } = useSession();
|
|
951
|
-
const [confirmed, setConfirmed] =
|
|
952
|
-
|
|
891
|
+
const [confirmed, setConfirmed] = React8.useState(null);
|
|
892
|
+
React8.useEffect(() => {
|
|
953
893
|
if (!needsMfaEnrollment) {
|
|
954
894
|
setConfirmed(null);
|
|
955
895
|
return;
|
|
@@ -974,22 +914,22 @@ function MfaEnrollmentStep({ title }) {
|
|
|
974
914
|
const t = useT();
|
|
975
915
|
const { refetch } = useSession();
|
|
976
916
|
return /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
979
|
-
/* @__PURE__ */
|
|
917
|
+
/* @__PURE__ */ jsx10("h2", { className: "ba-title", children: title ?? t("mfaGate.title") }),
|
|
918
|
+
/* @__PURE__ */ jsx10("p", { className: "ba-muted", children: t("mfaGate.body") }),
|
|
919
|
+
/* @__PURE__ */ jsx10(MFAEnrollment, { onEnrolled: () => refetch({ query: { disableCookieCache: true } }) })
|
|
980
920
|
] });
|
|
981
921
|
}
|
|
982
922
|
|
|
983
923
|
// src/components/AuthOwlBadge.tsx
|
|
984
|
-
import { jsx as
|
|
924
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
985
925
|
function OwlMark() {
|
|
986
926
|
return /* @__PURE__ */ jsxs11("svg", { className: "ba-badge-mark", viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: [
|
|
987
|
-
/* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
990
|
-
/* @__PURE__ */
|
|
991
|
-
/* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
927
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "11", fill: "#F5B84C" }),
|
|
928
|
+
/* @__PURE__ */ jsx11("circle", { cx: "8.4", cy: "10", r: "3", fill: "#fff" }),
|
|
929
|
+
/* @__PURE__ */ jsx11("circle", { cx: "15.6", cy: "10", r: "3", fill: "#fff" }),
|
|
930
|
+
/* @__PURE__ */ jsx11("circle", { cx: "8.4", cy: "10", r: "1.4", fill: "#1f1300" }),
|
|
931
|
+
/* @__PURE__ */ jsx11("circle", { cx: "15.6", cy: "10", r: "1.4", fill: "#1f1300" }),
|
|
932
|
+
/* @__PURE__ */ jsx11("path", { d: "M12 13.1l1.5 2.1h-3z", fill: "#1f1300" })
|
|
993
933
|
] });
|
|
994
934
|
}
|
|
995
935
|
function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
|
|
@@ -1005,9 +945,9 @@ function AuthOwlBadge({ href = "https://authowl.dev", force } = {}) {
|
|
|
1005
945
|
rel: "noopener noreferrer",
|
|
1006
946
|
"data-testid": "authowl-badge",
|
|
1007
947
|
children: [
|
|
1008
|
-
/* @__PURE__ */
|
|
1009
|
-
/* @__PURE__ */
|
|
1010
|
-
brand: /* @__PURE__ */
|
|
948
|
+
/* @__PURE__ */ jsx11(OwlMark, {}),
|
|
949
|
+
/* @__PURE__ */ jsx11("span", { children: richMessage(t("badge.securedBy"), {
|
|
950
|
+
brand: /* @__PURE__ */ jsx11("span", { className: "ba-badge-brand", children: "AuthOwl" })
|
|
1011
951
|
}) })
|
|
1012
952
|
]
|
|
1013
953
|
}
|
|
@@ -1022,19 +962,19 @@ import {
|
|
|
1022
962
|
} from "@authowl/core";
|
|
1023
963
|
|
|
1024
964
|
// src/components/ConsentDocLinks.tsx
|
|
1025
|
-
import { Fragment as Fragment3, jsx as
|
|
965
|
+
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1026
966
|
function ConsentDocLinks({ termsUrl, privacyUrl }) {
|
|
1027
967
|
const t = useT();
|
|
1028
968
|
if (!termsUrl && !privacyUrl) return null;
|
|
1029
969
|
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
1030
|
-
termsUrl && /* @__PURE__ */
|
|
970
|
+
termsUrl && /* @__PURE__ */ jsx12("a", { href: termsUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.termsOfService") }),
|
|
1031
971
|
termsUrl && privacyUrl && t("consent.docJoiner"),
|
|
1032
|
-
privacyUrl && /* @__PURE__ */
|
|
972
|
+
privacyUrl && /* @__PURE__ */ jsx12("a", { href: privacyUrl, target: "_blank", rel: "noopener noreferrer", children: t("consent.privacyPolicy") })
|
|
1033
973
|
] });
|
|
1034
974
|
}
|
|
1035
975
|
|
|
1036
976
|
// src/components/LegalConsentCheckbox.tsx
|
|
1037
|
-
import { jsx as
|
|
977
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1038
978
|
function LegalConsentCheckbox({
|
|
1039
979
|
legal,
|
|
1040
980
|
accepted,
|
|
@@ -1045,7 +985,7 @@ function LegalConsentCheckbox({
|
|
|
1045
985
|
if (!legal.required) return null;
|
|
1046
986
|
return /* @__PURE__ */ jsxs13("label", { className: "ba-consent", "data-testid": testId, children: [
|
|
1047
987
|
/* @__PURE__ */ jsxs13("span", { className: "ba-checkbox-control", children: [
|
|
1048
|
-
/* @__PURE__ */
|
|
988
|
+
/* @__PURE__ */ jsx13(
|
|
1049
989
|
"input",
|
|
1050
990
|
{
|
|
1051
991
|
className: "ba-checkbox",
|
|
@@ -1054,14 +994,69 @@ function LegalConsentCheckbox({
|
|
|
1054
994
|
onChange: (event) => onAcceptedChange(event.target.checked)
|
|
1055
995
|
}
|
|
1056
996
|
),
|
|
1057
|
-
/* @__PURE__ */
|
|
997
|
+
/* @__PURE__ */ jsx13("span", { className: "ba-checkbox-visual", "aria-hidden": "true", children: /* @__PURE__ */ jsx13("span", { className: "ba-checkbox-check" }) })
|
|
1058
998
|
] }),
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
links: /* @__PURE__ */
|
|
999
|
+
/* @__PURE__ */ jsx13("span", { children: richMessage(t("signUp.consentLabel"), {
|
|
1000
|
+
links: /* @__PURE__ */ jsx13(ConsentDocLinks, { termsUrl: legal.termsUrl, privacyUrl: legal.privacyUrl })
|
|
1061
1001
|
}) })
|
|
1062
1002
|
] });
|
|
1063
1003
|
}
|
|
1064
1004
|
|
|
1005
|
+
// src/components/Turnstile.tsx
|
|
1006
|
+
import * as React9 from "react";
|
|
1007
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1008
|
+
var LOCAL_TURNSTILE_TEST_TOKEN = "XXXX.DUMMY.TOKEN.XXXX";
|
|
1009
|
+
async function loadTurnstile() {
|
|
1010
|
+
const [{ loadCaptcha }, { CAPTCHA_ADAPTERS }] = await Promise.all([
|
|
1011
|
+
import("./captcha-loader-VNULARCZ.js"),
|
|
1012
|
+
import("./captcha-providers-STYFL25P.js")
|
|
1013
|
+
]);
|
|
1014
|
+
const adapter = CAPTCHA_ADAPTERS.turnstile;
|
|
1015
|
+
return { api: await loadCaptcha(adapter), adapter };
|
|
1016
|
+
}
|
|
1017
|
+
function Turnstile({
|
|
1018
|
+
siteKey,
|
|
1019
|
+
theme,
|
|
1020
|
+
onToken,
|
|
1021
|
+
onUnavailable
|
|
1022
|
+
}) {
|
|
1023
|
+
const container = React9.useRef(null);
|
|
1024
|
+
const callbacks = React9.useRef({ onToken, onUnavailable });
|
|
1025
|
+
callbacks.current = { onToken, onUnavailable };
|
|
1026
|
+
React9.useEffect(() => {
|
|
1027
|
+
callbacks.current.onToken(null);
|
|
1028
|
+
if (!siteKey) {
|
|
1029
|
+
callbacks.current.onToken(LOCAL_TURNSTILE_TEST_TOKEN);
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
let active = true;
|
|
1033
|
+
let widget = null;
|
|
1034
|
+
void loadTurnstile().then(({ api, adapter }) => {
|
|
1035
|
+
if (!active || !container.current) return;
|
|
1036
|
+
const id = api.render(container.current, {
|
|
1037
|
+
sitekey: siteKey,
|
|
1038
|
+
theme: theme === "system" ? "auto" : theme,
|
|
1039
|
+
size: "flexible",
|
|
1040
|
+
callback: (token) => callbacks.current.onToken(token),
|
|
1041
|
+
"expired-callback": () => callbacks.current.onToken(null),
|
|
1042
|
+
"error-callback": () => {
|
|
1043
|
+
callbacks.current.onToken(null);
|
|
1044
|
+
callbacks.current.onUnavailable();
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
widget = { api, adapter, id };
|
|
1048
|
+
}).catch(() => {
|
|
1049
|
+
if (active) callbacks.current.onUnavailable();
|
|
1050
|
+
});
|
|
1051
|
+
return () => {
|
|
1052
|
+
active = false;
|
|
1053
|
+
if (widget) widget.adapter.teardown(widget.api, widget.id);
|
|
1054
|
+
};
|
|
1055
|
+
}, [siteKey, theme]);
|
|
1056
|
+
if (!siteKey) return null;
|
|
1057
|
+
return /* @__PURE__ */ jsx14("div", { className: "ba-turnstile", ref: container, "data-testid": "phoneotp-turnstile" });
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1065
1060
|
// src/components/PhoneOTP.tsx
|
|
1066
1061
|
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1067
1062
|
function PhoneOTP({
|
|
@@ -1425,6 +1420,15 @@ function SignIn({
|
|
|
1425
1420
|
const [challenge, setChallenge] = React13.useState(false);
|
|
1426
1421
|
const mfaEnrolment = useConfirmedMfaPending("clear");
|
|
1427
1422
|
const emailRef = React13.useRef(null);
|
|
1423
|
+
const recordSignInMethod = useSignInMethodRecorder();
|
|
1424
|
+
const onPasskeySignedIn = React13.useCallback(() => {
|
|
1425
|
+
recordSignInMethod("passkey");
|
|
1426
|
+
onSignedIn?.();
|
|
1427
|
+
}, [onSignedIn, recordSignInMethod]);
|
|
1428
|
+
const onPhoneOtpSignedIn = React13.useCallback(() => {
|
|
1429
|
+
recordSignInMethod("phone-otp");
|
|
1430
|
+
onSignedIn?.();
|
|
1431
|
+
}, [onSignedIn, recordSignInMethod]);
|
|
1428
1432
|
const plan = resolveSignInMethods(
|
|
1429
1433
|
config,
|
|
1430
1434
|
typeof window === "undefined" ? void 0 : window.location.hostname
|
|
@@ -1432,7 +1436,7 @@ function SignIn({
|
|
|
1432
1436
|
usePasskeyAutofill({
|
|
1433
1437
|
enabled: credentialMode === "email" && plan.autofillHost !== null,
|
|
1434
1438
|
redirectTo,
|
|
1435
|
-
onSignedIn
|
|
1439
|
+
onSignedIn: onPasskeySignedIn
|
|
1436
1440
|
});
|
|
1437
1441
|
const badge = /* @__PURE__ */ jsx19(AuthOwlBadge, { force: showBadge });
|
|
1438
1442
|
const branding = showBranding ? /* @__PURE__ */ jsx19(AuthOwlBranding, {}) : null;
|
|
@@ -1484,7 +1488,10 @@ function SignIn({
|
|
|
1484
1488
|
error,
|
|
1485
1489
|
onSubmit: () => void run(() => signInEmailOtp({ email, otp }), {
|
|
1486
1490
|
failure: t("emailOtp.error.invalidCode"),
|
|
1487
|
-
onSuccess: () =>
|
|
1491
|
+
onSuccess: () => {
|
|
1492
|
+
recordSignInMethod("email-otp");
|
|
1493
|
+
finishSignIn({ sessionStore, redirectTo, onSignedIn });
|
|
1494
|
+
}
|
|
1488
1495
|
}),
|
|
1489
1496
|
onChangeEmail: () => {
|
|
1490
1497
|
setOtp("");
|
|
@@ -1512,7 +1519,7 @@ function SignIn({
|
|
|
1512
1519
|
PhoneOTP,
|
|
1513
1520
|
{
|
|
1514
1521
|
redirectTo,
|
|
1515
|
-
onSignedIn,
|
|
1522
|
+
onSignedIn: onPhoneOtpSignedIn,
|
|
1516
1523
|
onBack: () => setView("sign-in"),
|
|
1517
1524
|
onMfaPasswordRequired: () => {
|
|
1518
1525
|
setView("sign-in");
|
|
@@ -1540,6 +1547,7 @@ function SignIn({
|
|
|
1540
1547
|
failure: t("signIn.error.failed"),
|
|
1541
1548
|
onSuccess: (res) => {
|
|
1542
1549
|
const data = res.data;
|
|
1550
|
+
recordSignInMethod(credentialMode === "username" ? "username" : "password");
|
|
1543
1551
|
if (data && "twoFactorRedirect" in data && data.twoFactorRedirect) {
|
|
1544
1552
|
setChallenge(true);
|
|
1545
1553
|
} else {
|
|
@@ -1552,7 +1560,10 @@ function SignIn({
|
|
|
1552
1560
|
"magic",
|
|
1553
1561
|
() => void run(() => authChallenge.run(AUTH_CHALLENGE_ACTIONS.passwordless, (options) => signInMagicLink({ email, callbackURL: redirectTo }, options)), {
|
|
1554
1562
|
failure: t("magicLink.error.sendFailed"),
|
|
1555
|
-
onSuccess: () =>
|
|
1563
|
+
onSuccess: () => {
|
|
1564
|
+
recordSignInMethod("magic-link", true);
|
|
1565
|
+
setView("magic-sent");
|
|
1566
|
+
}
|
|
1556
1567
|
})
|
|
1557
1568
|
);
|
|
1558
1569
|
const doRequestOtp = () => start(
|
|
@@ -1569,6 +1580,7 @@ function SignIn({
|
|
|
1569
1580
|
{
|
|
1570
1581
|
failure: t("sso.error.startFailed"),
|
|
1571
1582
|
mapError: (error2) => error2.status === 404 ? t("sso.error.notFound") : null,
|
|
1583
|
+
onSuccess: () => recordSignInMethod("sso", true),
|
|
1572
1584
|
// SSO always redirects the browser to the IdP; keep the spinner up
|
|
1573
1585
|
// through that navigation instead of flashing back to idle.
|
|
1574
1586
|
keepPendingOnSuccess: true
|
|
@@ -1707,7 +1719,7 @@ function SignIn({
|
|
|
1707
1719
|
]
|
|
1708
1720
|
}
|
|
1709
1721
|
),
|
|
1710
|
-
plan.passkey && /* @__PURE__ */ jsx19(PasskeyButton, { redirectTo, onSignedIn }),
|
|
1722
|
+
plan.passkey && /* @__PURE__ */ jsx19(PasskeyButton, { redirectTo, onSignedIn: onPasskeySignedIn }),
|
|
1711
1723
|
plan.phoneOtp && /* @__PURE__ */ jsx19(
|
|
1712
1724
|
"button",
|
|
1713
1725
|
{
|
|
@@ -3449,9 +3461,9 @@ function formatSessionTime(date, locale) {
|
|
|
3449
3461
|
|
|
3450
3462
|
// src/components/user-profile/use-account-resource.ts
|
|
3451
3463
|
import * as React32 from "react";
|
|
3452
|
-
function useAccountResource(
|
|
3453
|
-
const loaderRef = React32.useRef(
|
|
3454
|
-
loaderRef.current =
|
|
3464
|
+
function useAccountResource(loader, failure) {
|
|
3465
|
+
const loaderRef = React32.useRef(loader);
|
|
3466
|
+
loaderRef.current = loader;
|
|
3455
3467
|
const failureRef = React32.useRef(failure);
|
|
3456
3468
|
failureRef.current = failure;
|
|
3457
3469
|
const toServerError = useServerError();
|
|
@@ -4443,7 +4455,7 @@ var SECTION_KEYS2 = {
|
|
|
4443
4455
|
invitations: "organization.profile.nav.invitations",
|
|
4444
4456
|
danger: "organization.profile.nav.danger"
|
|
4445
4457
|
};
|
|
4446
|
-
var TeamsSection = React44.lazy(() => import("./TeamsSection-
|
|
4458
|
+
var TeamsSection = React44.lazy(() => import("./TeamsSection-NV64O5I4.js"));
|
|
4447
4459
|
function OrganizationProfile({ organizationId, defaultSection = "general", onDeleted, onLeft } = {}) {
|
|
4448
4460
|
const t = useT();
|
|
4449
4461
|
const toServerError = useServerError();
|
|
@@ -5010,6 +5022,14 @@ function normalizeDismissReason(reason) {
|
|
|
5010
5022
|
}
|
|
5011
5023
|
return "unknown";
|
|
5012
5024
|
}
|
|
5025
|
+
var warnedMissingNonce = /* @__PURE__ */ new Set();
|
|
5026
|
+
function warnOneTapWithoutNonce(clientId) {
|
|
5027
|
+
if (warnedMissingNonce.has(clientId)) return;
|
|
5028
|
+
warnedMissingNonce.add(clientId);
|
|
5029
|
+
console.warn(
|
|
5030
|
+
"[AuthOwl] <GoogleOneTap/> is running without a `nonce`. Google\u2019s ID token is then not bound to a value chosen for this prompt. Generate a fresh random value per prompt and pass it as `nonce`; AuthOwl forwards it to Google and verifies the match. The direct browser exchange does not keep separate one-time server state, so do not treat this prop by itself as replay protection. (This warning is dev-only.)"
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
5013
5033
|
function GoogleOneTap({
|
|
5014
5034
|
disabled = false,
|
|
5015
5035
|
nonce,
|
|
@@ -5033,6 +5053,11 @@ function GoogleOneTap({
|
|
|
5033
5053
|
callbacks.current = { onSignedIn, onSkipped, onDismissed, onError };
|
|
5034
5054
|
const googleEnabled = config?.socialProviders.includes("google") === true;
|
|
5035
5055
|
const clientId = config?.socialProviderClientIds?.google;
|
|
5056
|
+
React47.useEffect(() => {
|
|
5057
|
+
if (process.env.NODE_ENV === "production") return;
|
|
5058
|
+
if (disabled || !googleEnabled || !clientId || nonce) return;
|
|
5059
|
+
warnOneTapWithoutNonce(clientId);
|
|
5060
|
+
}, [clientId, disabled, googleEnabled, nonce]);
|
|
5036
5061
|
React47.useEffect(() => {
|
|
5037
5062
|
if (configLoading || !sessionLoaded) return;
|
|
5038
5063
|
if (disabled) {
|
|
@@ -5208,6 +5233,7 @@ export {
|
|
|
5208
5233
|
useConsent,
|
|
5209
5234
|
useEmailVerification,
|
|
5210
5235
|
useInvitationRecipientHint,
|
|
5236
|
+
useLastUsedSignInMethod,
|
|
5211
5237
|
useLocale,
|
|
5212
5238
|
useMFA,
|
|
5213
5239
|
useOrganization,
|