@authowl/react 0.18.1 → 0.18.3

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.cjs CHANGED
@@ -625,7 +625,21 @@ var KNOWN_METHODS = [
625
625
  function emailAutocomplete(isPasskeyHost) {
626
626
  return isPasskeyHost ? "email webauthn" : "email";
627
627
  }
628
- function resolveSignInMethods(config) {
628
+ function passkeyReachableFrom(authBaseUrl, pageHost, relyingPartyId) {
629
+ if (pageHost === void 0) return true;
630
+ let rpId = relyingPartyId ?? void 0;
631
+ if (!rpId) {
632
+ if (!authBaseUrl) return true;
633
+ try {
634
+ rpId = new URL(authBaseUrl).hostname;
635
+ } catch {
636
+ return true;
637
+ }
638
+ }
639
+ if (rpId !== "localhost" && !rpId.includes(".")) return false;
640
+ return pageHost === rpId || pageHost.endsWith(`.${rpId}`);
641
+ }
642
+ function resolveSignInMethods(config, pageHost) {
629
643
  const methods = config?.enabledMethods ?? ["password"];
630
644
  const social = config?.socialProviders ?? [];
631
645
  const has = (m) => methods.includes(m);
@@ -635,7 +649,11 @@ function resolveSignInMethods(config) {
635
649
  const magicLink = capabilities.magicLinkSignIn;
636
650
  const emailOtp = capabilities.emailOtpSignIn && !capabilities.totp && !capabilities.mfaRequired;
637
651
  const phoneOtp = capabilities.phoneSignIn;
638
- const passkey = capabilities.passkeySignIn;
652
+ const passkey = capabilities.passkeySignIn && passkeyReachableFrom(
653
+ config?.authBaseUrl,
654
+ pageHost,
655
+ config?.authentication?.passkey?.relyingPartyId
656
+ );
639
657
  const sso = has("sso");
640
658
  const renderable = password || username || magicLink || emailOtp || phoneOtp || passkey || sso || social.length > 0;
641
659
  const emptyReason = renderable ? null : methods.length > 0 ? "unsupported" : "none";
@@ -688,8 +706,15 @@ function hasUsableSession(store) {
688
706
  const data = store.getSnapshot().data;
689
707
  return Boolean(data?.user && data.session.pendingMfaEnrollment !== true);
690
708
  }
709
+ function isHeldAtEnrollment(store) {
710
+ const data = store.getSnapshot().data;
711
+ return Boolean(data?.user && data.session.pendingMfaEnrollment === true);
712
+ }
691
713
  function waitForUsableSession(store) {
692
- if (hasUsableSession(store)) return Promise.resolve();
714
+ const outcome = () => isHeldAtEnrollment(store) ? "held" : "settled";
715
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) {
716
+ return Promise.resolve(outcome());
717
+ }
693
718
  return new Promise((resolve) => {
694
719
  let settled = false;
695
720
  let unsubscribe = () => {
@@ -699,23 +724,22 @@ function waitForUsableSession(store) {
699
724
  settled = true;
700
725
  window.clearTimeout(timeout);
701
726
  unsubscribe();
702
- resolve();
727
+ resolve(outcome());
703
728
  };
704
729
  const timeout = window.setTimeout(finish, SESSION_SETTLE_TIMEOUT_MS);
705
730
  unsubscribe = store.subscribe(() => {
706
- if (hasUsableSession(store)) finish();
731
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) finish();
707
732
  });
708
- if (hasUsableSession(store)) finish();
733
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) finish();
709
734
  });
710
735
  }
711
736
  async function finishSignIn(opts) {
737
+ if (await waitForUsableSession(opts.sessionStore) === "held") return;
712
738
  opts.onSignedIn?.();
713
739
  if (safeRedirect(opts.redirectTo)) {
714
740
  await new Promise(() => {
715
741
  });
716
- return;
717
742
  }
718
- await waitForUsableSession(opts.sessionStore);
719
743
  }
720
744
 
721
745
  // src/components/use-submit-action.ts
@@ -3623,7 +3647,10 @@ function SignIn({
3623
3647
  const [challenge, setChallenge] = React16.useState(false);
3624
3648
  const mfaEnrolment = useConfirmedMfaPending();
3625
3649
  const emailRef = React16.useRef(null);
3626
- const plan = resolveSignInMethods(config);
3650
+ const plan = resolveSignInMethods(
3651
+ config,
3652
+ typeof window === "undefined" ? void 0 : window.location.hostname
3653
+ );
3627
3654
  usePasskeyAutofill({
3628
3655
  enabled: credentialMode === "email" && plan.autofillHost !== null,
3629
3656
  redirectTo,
package/dist/index.d.cts CHANGED
@@ -311,12 +311,9 @@ type SignInPlan = {
311
311
  * passkeys inline. One place owns the token string.
312
312
  */
313
313
  declare function emailAutocomplete(isPasskeyHost: boolean): string;
314
- /**
315
- * Resolve the sign-in surfaces from a project's public config. On a config error
316
- * (`config` null) fall back to password - the safe default that always renders -
317
- * rather than showing nothing.
318
- */
319
- declare function resolveSignInMethods(config: PublicConfig | null): SignInPlan;
314
+ declare function resolveSignInMethods(config: PublicConfig | null,
315
+ /** The hostname the form is rendering on; omit when it is not knowable. */
316
+ pageHost?: string): SignInPlan;
320
317
 
321
318
  type SignInProps = {
322
319
  redirectTo?: string;
package/dist/index.d.ts CHANGED
@@ -311,12 +311,9 @@ type SignInPlan = {
311
311
  * passkeys inline. One place owns the token string.
312
312
  */
313
313
  declare function emailAutocomplete(isPasskeyHost: boolean): string;
314
- /**
315
- * Resolve the sign-in surfaces from a project's public config. On a config error
316
- * (`config` null) fall back to password - the safe default that always renders -
317
- * rather than showing nothing.
318
- */
319
- declare function resolveSignInMethods(config: PublicConfig | null): SignInPlan;
314
+ declare function resolveSignInMethods(config: PublicConfig | null,
315
+ /** The hostname the form is rendering on; omit when it is not knowable. */
316
+ pageHost?: string): SignInPlan;
320
317
 
321
318
  type SignInProps = {
322
319
  redirectTo?: string;
package/dist/index.js CHANGED
@@ -544,7 +544,21 @@ var KNOWN_METHODS = [
544
544
  function emailAutocomplete(isPasskeyHost) {
545
545
  return isPasskeyHost ? "email webauthn" : "email";
546
546
  }
547
- function resolveSignInMethods(config) {
547
+ function passkeyReachableFrom(authBaseUrl, pageHost, relyingPartyId) {
548
+ if (pageHost === void 0) return true;
549
+ let rpId = relyingPartyId ?? void 0;
550
+ if (!rpId) {
551
+ if (!authBaseUrl) return true;
552
+ try {
553
+ rpId = new URL(authBaseUrl).hostname;
554
+ } catch {
555
+ return true;
556
+ }
557
+ }
558
+ if (rpId !== "localhost" && !rpId.includes(".")) return false;
559
+ return pageHost === rpId || pageHost.endsWith(`.${rpId}`);
560
+ }
561
+ function resolveSignInMethods(config, pageHost) {
548
562
  const methods = config?.enabledMethods ?? ["password"];
549
563
  const social = config?.socialProviders ?? [];
550
564
  const has = (m) => methods.includes(m);
@@ -554,7 +568,11 @@ function resolveSignInMethods(config) {
554
568
  const magicLink = capabilities.magicLinkSignIn;
555
569
  const emailOtp = capabilities.emailOtpSignIn && !capabilities.totp && !capabilities.mfaRequired;
556
570
  const phoneOtp = capabilities.phoneSignIn;
557
- const passkey = capabilities.passkeySignIn;
571
+ const passkey = capabilities.passkeySignIn && passkeyReachableFrom(
572
+ config?.authBaseUrl,
573
+ pageHost,
574
+ config?.authentication?.passkey?.relyingPartyId
575
+ );
558
576
  const sso = has("sso");
559
577
  const renderable = password || username || magicLink || emailOtp || phoneOtp || passkey || sso || social.length > 0;
560
578
  const emptyReason = renderable ? null : methods.length > 0 ? "unsupported" : "none";
@@ -607,8 +625,15 @@ function hasUsableSession(store) {
607
625
  const data = store.getSnapshot().data;
608
626
  return Boolean(data?.user && data.session.pendingMfaEnrollment !== true);
609
627
  }
628
+ function isHeldAtEnrollment(store) {
629
+ const data = store.getSnapshot().data;
630
+ return Boolean(data?.user && data.session.pendingMfaEnrollment === true);
631
+ }
610
632
  function waitForUsableSession(store) {
611
- if (hasUsableSession(store)) return Promise.resolve();
633
+ const outcome = () => isHeldAtEnrollment(store) ? "held" : "settled";
634
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) {
635
+ return Promise.resolve(outcome());
636
+ }
612
637
  return new Promise((resolve) => {
613
638
  let settled = false;
614
639
  let unsubscribe = () => {
@@ -618,23 +643,22 @@ function waitForUsableSession(store) {
618
643
  settled = true;
619
644
  window.clearTimeout(timeout);
620
645
  unsubscribe();
621
- resolve();
646
+ resolve(outcome());
622
647
  };
623
648
  const timeout = window.setTimeout(finish, SESSION_SETTLE_TIMEOUT_MS);
624
649
  unsubscribe = store.subscribe(() => {
625
- if (hasUsableSession(store)) finish();
650
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) finish();
626
651
  });
627
- if (hasUsableSession(store)) finish();
652
+ if (hasUsableSession(store) || isHeldAtEnrollment(store)) finish();
628
653
  });
629
654
  }
630
655
  async function finishSignIn(opts) {
656
+ if (await waitForUsableSession(opts.sessionStore) === "held") return;
631
657
  opts.onSignedIn?.();
632
658
  if (safeRedirect(opts.redirectTo)) {
633
659
  await new Promise(() => {
634
660
  });
635
- return;
636
661
  }
637
- await waitForUsableSession(opts.sessionStore);
638
662
  }
639
663
 
640
664
  // src/components/use-submit-action.ts
@@ -3545,7 +3569,10 @@ function SignIn({
3545
3569
  const [challenge, setChallenge] = React16.useState(false);
3546
3570
  const mfaEnrolment = useConfirmedMfaPending();
3547
3571
  const emailRef = React16.useRef(null);
3548
- const plan = resolveSignInMethods(config);
3572
+ const plan = resolveSignInMethods(
3573
+ config,
3574
+ typeof window === "undefined" ? void 0 : window.location.hostname
3575
+ );
3549
3576
  usePasskeyAutofill({
3550
3577
  enabled: credentialMode === "email" && plan.autofillHost !== null,
3551
3578
  redirectTo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authowl/react",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "React provider, hooks, and drop-in components for AuthOwl, the multi-tenant auth SaaS.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -47,7 +47,7 @@
47
47
  "LICENSE"
48
48
  ],
49
49
  "dependencies": {
50
- "@authowl/core": "0.15.0"
50
+ "@authowl/core": "0.15.1"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": ">=18",