@nauth-toolkit/client 0.1.57 → 0.1.59

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
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  AuthAuditEventType: () => AuthAuditEventType,
26
26
  AuthChallenge: () => AuthChallenge,
27
27
  BrowserStorage: () => BrowserStorage,
28
+ ChallengeRouter: () => ChallengeRouter,
28
29
  EventEmitter: () => EventEmitter,
29
30
  FetchAdapter: () => FetchAdapter,
30
31
  InMemoryStorage: () => InMemoryStorage,
@@ -562,6 +563,155 @@ var FetchAdapter = class {
562
563
  }
563
564
  };
564
565
 
566
+ // src/core/challenge-router.ts
567
+ var ChallengeRouter = class {
568
+ constructor(config) {
569
+ this.config = config;
570
+ }
571
+ /**
572
+ * Handle auth response - either call callback or auto-navigate.
573
+ *
574
+ * @param response - Auth response from backend
575
+ * @param context - Context about the auth operation
576
+ */
577
+ async handleAuthResponse(response, context) {
578
+ if (this.config.onAuthResponse) {
579
+ await this.config.onAuthResponse(response, context);
580
+ return;
581
+ }
582
+ if (response.challengeName) {
583
+ await this.navigateToChallenge(response);
584
+ } else {
585
+ await this.navigateToSuccess();
586
+ }
587
+ }
588
+ /**
589
+ * Navigate to appropriate challenge route.
590
+ *
591
+ * @param response - Auth response containing challenge info
592
+ */
593
+ async navigateToChallenge(response) {
594
+ const url = this.buildChallengeUrl(response);
595
+ await this.navigate(url);
596
+ }
597
+ /**
598
+ * Navigate to success URL.
599
+ */
600
+ async navigateToSuccess() {
601
+ const url = this.config.redirects?.success || "/";
602
+ await this.navigate(url);
603
+ }
604
+ /**
605
+ * Navigate to error URL.
606
+ *
607
+ * @param type - Type of error (oauth or session)
608
+ */
609
+ async navigateToError(type) {
610
+ const url = type === "oauth" ? this.config.redirects?.oauthError || "/login" : this.config.redirects?.sessionExpired || "/login";
611
+ await this.navigate(url);
612
+ }
613
+ /**
614
+ * Build challenge URL based on configuration.
615
+ *
616
+ * Priority:
617
+ * 1. Custom route mapping (challengeRoutes)
618
+ * 2. Single route with query param (useSingleChallengeRoute)
619
+ * 3. MFA-specific routes (mfaRoutes) - for MFA_REQUIRED challenge only
620
+ * 4. Default separate routes (challengeBase + kebab-case)
621
+ *
622
+ * @param response - Auth response containing challenge info
623
+ * @returns URL to navigate to
624
+ */
625
+ buildChallengeUrl(response) {
626
+ const challengeName = response.challengeName;
627
+ if (this.config.redirects?.challengeRoutes?.[challengeName]) {
628
+ return this.config.redirects.challengeRoutes[challengeName];
629
+ }
630
+ const base = this.config.redirects?.challengeBase || "/auth/challenge";
631
+ if (this.config.redirects?.useSingleChallengeRoute) {
632
+ return `${base}?challenge=${challengeName}`;
633
+ }
634
+ if (challengeName === "MFA_REQUIRED" /* MFA_REQUIRED */) {
635
+ const mfaUrl = this.buildMFAUrl(response);
636
+ if (mfaUrl) {
637
+ return mfaUrl;
638
+ }
639
+ }
640
+ const route = this.buildDefaultRouteSegment(challengeName, response);
641
+ return `${base}/${route}`;
642
+ }
643
+ /**
644
+ * Build MFA-specific URL if custom mfaRoutes are configured.
645
+ *
646
+ * @param response - Auth response with MFA challenge parameters
647
+ * @returns Custom MFA URL if configured, null otherwise
648
+ */
649
+ buildMFAUrl(response) {
650
+ const params = response.challengeParameters;
651
+ const method = params?.["preferredMethod"] || params?.["method"];
652
+ const mfaRoutes = this.config.redirects?.mfaRoutes;
653
+ if (!mfaRoutes) {
654
+ return null;
655
+ }
656
+ if (method === "passkey" && mfaRoutes.passkey) {
657
+ return mfaRoutes.passkey;
658
+ }
659
+ if (!method && params?.["availableMethods"] && Array.isArray(params["availableMethods"]) && params["availableMethods"].length > 1) {
660
+ if (mfaRoutes.selector) {
661
+ return mfaRoutes.selector;
662
+ }
663
+ }
664
+ if (mfaRoutes.default) {
665
+ return mfaRoutes.default;
666
+ }
667
+ return null;
668
+ }
669
+ /**
670
+ * Build default route segment for a challenge.
671
+ *
672
+ * @param challengeName - Challenge type
673
+ * @param response - Auth response for extracting challenge parameters (needed for MFA)
674
+ * @returns Route segment (e.g., 'mfa-required/passkey', 'verify-email')
675
+ */
676
+ buildDefaultRouteSegment(challengeName, response) {
677
+ if (challengeName === "MFA_REQUIRED" /* MFA_REQUIRED */ && response) {
678
+ const params = response.challengeParameters;
679
+ const method = params?.["preferredMethod"] || params?.["method"];
680
+ if (method === "passkey") {
681
+ return "mfa-required/passkey";
682
+ }
683
+ if (!method && params?.["availableMethods"] && Array.isArray(params["availableMethods"]) && params["availableMethods"].length > 1) {
684
+ return "mfa-selector";
685
+ }
686
+ return "mfa-required";
687
+ }
688
+ return challengeName.toLowerCase().replace(/_/g, "-");
689
+ }
690
+ /**
691
+ * Execute navigation using configured handler or default.
692
+ *
693
+ * @param url - URL to navigate to
694
+ */
695
+ async navigate(url) {
696
+ if (this.config.navigationHandler) {
697
+ await this.config.navigationHandler(url);
698
+ } else {
699
+ if (typeof window !== "undefined") {
700
+ window.location.replace(url);
701
+ }
702
+ }
703
+ }
704
+ /**
705
+ * Expose URL builder for guards/components that need it.
706
+ *
707
+ * @param response - Auth response containing challenge info
708
+ * @returns URL for the challenge
709
+ */
710
+ getChallengeUrl(response) {
711
+ return this.buildChallengeUrl(response);
712
+ }
713
+ };
714
+
565
715
  // src/core/client.ts
566
716
  var USER_KEY2 = "nauth_user";
567
717
  var CHALLENGE_KEY2 = "nauth_challenge_session";
@@ -582,6 +732,7 @@ var NAuthClient = class {
582
732
  __publicField(this, "config");
583
733
  __publicField(this, "tokenManager");
584
734
  __publicField(this, "eventEmitter");
735
+ __publicField(this, "challengeRouter");
585
736
  __publicField(this, "currentUser", null);
586
737
  /**
587
738
  * Handle cross-tab storage updates.
@@ -600,6 +751,7 @@ var NAuthClient = class {
600
751
  this.config = resolveConfig({ ...userConfig, storage }, defaultAdapter);
601
752
  this.tokenManager = new TokenManager(storage);
602
753
  this.eventEmitter = new EventEmitter();
754
+ this.challengeRouter = new ChallengeRouter(this.config);
603
755
  if (hasWindow()) {
604
756
  window.addEventListener("storage", this.handleStorageEvent);
605
757
  }
@@ -629,6 +781,7 @@ var NAuthClient = class {
629
781
  const successEvent = { type: "auth:success", data: response, timestamp: Date.now() };
630
782
  this.eventEmitter.emit(successEvent);
631
783
  }
784
+ await this.challengeRouter.handleAuthResponse(response, { source: "login" });
632
785
  return response;
633
786
  } catch (error) {
634
787
  const authError = error instanceof NAuthClientError ? error : new NAuthClientError("AUTH_INVALID_CREDENTIALS" /* AUTH_INVALID_CREDENTIALS */, error.message || "Login failed");
@@ -650,6 +803,7 @@ var NAuthClient = class {
650
803
  } else {
651
804
  this.eventEmitter.emit({ type: "auth:success", data: response, timestamp: Date.now() });
652
805
  }
806
+ await this.challengeRouter.handleAuthResponse(response, { source: "signup" });
653
807
  return response;
654
808
  } catch (error) {
655
809
  const authError = error instanceof NAuthClientError ? error : new NAuthClientError("AUTH_INVALID_CREDENTIALS" /* AUTH_INVALID_CREDENTIALS */, error.message || "Signup failed");
@@ -779,6 +933,7 @@ var NAuthClient = class {
779
933
  const successEvent = { type: "auth:success", data: result, timestamp: Date.now() };
780
934
  this.eventEmitter.emit(successEvent);
781
935
  }
936
+ await this.challengeRouter.handleAuthResponse(result, { source: "challenge" });
782
937
  return result;
783
938
  } catch (error) {
784
939
  const authError = error instanceof NAuthClientError ? error : new NAuthClientError(
@@ -1026,6 +1181,7 @@ var NAuthClient = class {
1026
1181
  }
1027
1182
  const result = await this.post(this.config.endpoints.socialExchange, { exchangeToken: token });
1028
1183
  await this.handleAuthResponse(result);
1184
+ await this.challengeRouter.handleAuthResponse(result, { source: "social" });
1029
1185
  return result;
1030
1186
  }
1031
1187
  /**
@@ -1379,6 +1535,21 @@ var NAuthClient = class {
1379
1535
  });
1380
1536
  return response.data;
1381
1537
  }
1538
+ /**
1539
+ * Get challenge router for manual navigation control.
1540
+ * Useful for guards that need to handle errors or build custom URLs.
1541
+ *
1542
+ * @returns ChallengeRouter instance
1543
+ *
1544
+ * @example
1545
+ * ```typescript
1546
+ * const router = client.getChallengeRouter();
1547
+ * await router.navigateToError('oauth');
1548
+ * ```
1549
+ */
1550
+ getChallengeRouter() {
1551
+ return this.challengeRouter;
1552
+ }
1382
1553
  };
1383
1554
 
1384
1555
  // src/core/challenge-helpers.ts
@@ -1427,6 +1598,7 @@ function isOTPChallenge(challenge) {
1427
1598
  AuthAuditEventType,
1428
1599
  AuthChallenge,
1429
1600
  BrowserStorage,
1601
+ ChallengeRouter,
1430
1602
  EventEmitter,
1431
1603
  FetchAdapter,
1432
1604
  InMemoryStorage,