@djangocfg/api 2.1.457 → 2.1.460

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/auth.d.cts CHANGED
@@ -697,7 +697,7 @@ interface UseGithubAuthReturn {
697
697
  */
698
698
  declare const useGithubAuth: (options?: UseGithubAuthOptions) => UseGithubAuthReturn;
699
699
 
700
- interface UseTwoFactorOptions {
700
+ interface UseTwoFactorVerifyOptions {
701
701
  /** Callback on successful 2FA verification */
702
702
  onSuccess?: (user: any) => void;
703
703
  /** Callback on error */
@@ -707,7 +707,7 @@ interface UseTwoFactorOptions {
707
707
  /** Skip automatic redirect after success (caller handles navigation) */
708
708
  skipRedirect?: boolean;
709
709
  }
710
- interface UseTwoFactorReturn {
710
+ interface UseTwoFactorVerifyReturn {
711
711
  /** Loading state */
712
712
  isLoading: boolean;
713
713
  /** Error message */
@@ -726,31 +726,13 @@ interface UseTwoFactorReturn {
726
726
  clearError: () => void;
727
727
  }
728
728
  /**
729
- * Hook for 2FA verification during login.
730
- *
731
- * Usage:
732
- * 1. After OTP/OAuth verification returns requires_2fa=true and session_id
733
- * 2. Show 2FA form and collect TOTP code from user
734
- * 3. Call verifyTOTP(sessionId, code) to complete authentication
735
- *
736
- * @example
737
- * ```tsx
738
- * const { isLoading, error, verifyTOTP, verifyBackupCode } = useTwoFactor({
739
- * onSuccess: (user) => console.log('Logged in:', user),
740
- * onError: (error) => console.error(error),
741
- * redirectUrl: '/dashboard',
742
- * });
729
+ * 2FA verification during login (internal — composed by {@link useTwoFactor}).
743
730
  *
744
- * const handleSubmit = async (code: string) => {
745
- * if (useBackupCode) {
746
- * await verifyBackupCode(sessionId, code);
747
- * } else {
748
- * await verifyTOTP(sessionId, code);
749
- * }
750
- * };
751
- * ```
731
+ * After OTP/OAuth verification returns requires_2fa=true and a session_id,
732
+ * collect the TOTP (or backup) code and call verifyTOTP/verifyBackupCode to
733
+ * complete authentication.
752
734
  */
753
- declare const useTwoFactor: (options?: UseTwoFactorOptions) => UseTwoFactorReturn;
735
+ declare const useTwoFactorVerify: (options?: UseTwoFactorVerifyOptions) => UseTwoFactorVerifyReturn;
754
736
 
755
737
  interface TwoFactorSetupData {
756
738
  /** Device ID to use for confirmation */
@@ -793,38 +775,9 @@ interface UseTwoFactorSetupReturn {
793
775
  clearError: () => void;
794
776
  }
795
777
  /**
796
- * Hook for 2FA setup (enabling TOTP authentication).
797
- *
798
- * Flow:
799
- * 1. Call startSetup() to get QR code and provisioning URI
800
- * 2. User scans QR code with authenticator app
801
- * 3. Call confirmSetup(code) with the 6-digit code from app
802
- * 4. Show backup codes to user (they must save these!)
803
- *
804
- * @example
805
- * ```tsx
806
- * const {
807
- * isLoading,
808
- * error,
809
- * setupData,
810
- * backupCodes,
811
- * setupStep,
812
- * startSetup,
813
- * confirmSetup,
814
- * } = useTwoFactorSetup({
815
- * onComplete: (codes) => console.log('Backup codes:', codes),
816
- * onError: (error) => console.error(error),
817
- * });
818
- *
819
- * // Start setup
820
- * const data = await startSetup('My iPhone');
778
+ * 2FA setup enabling TOTP (internal, composed by {@link useTwoFactor}).
821
779
  *
822
- * // Show QR code
823
- * <QRCodeSVG value={data.provisioningUri} />
824
- *
825
- * // Confirm with code from app
826
- * const codes = await confirmSetup('123456');
827
- * ```
780
+ * Flow: startSetup() → user scans QR → confirmSetup(code) → show backup codes.
828
781
  */
829
782
  declare const useTwoFactorSetup: (options?: UseTwoFactorSetupOptions) => UseTwoFactorSetupReturn;
830
783
 
@@ -851,8 +804,49 @@ interface UseTwoFactorStatusReturn {
851
804
  /** Clear error */
852
805
  clearError: () => void;
853
806
  }
807
+ /**
808
+ * Read and manage 2FA status (internal, composed by {@link useTwoFactor}).
809
+ *
810
+ * Fetches the device list / enabled flag and disables 2FA.
811
+ */
854
812
  declare const useTwoFactorStatus: () => UseTwoFactorStatusReturn;
855
813
 
814
+ interface UseTwoFactorOptions {
815
+ /** Options for the login-time verification namespace */
816
+ verify?: UseTwoFactorVerifyOptions;
817
+ /** Options for the setup (enable) namespace */
818
+ setup?: UseTwoFactorSetupOptions;
819
+ }
820
+ interface UseTwoFactorReturn {
821
+ /** Verify a 2FA challenge during login (TOTP / backup code). */
822
+ verify: UseTwoFactorVerifyReturn;
823
+ /** Enable 2FA — QR provisioning → confirm → backup codes. */
824
+ setup: UseTwoFactorSetupReturn;
825
+ /** Read device list / enabled flag and disable 2FA. */
826
+ status: UseTwoFactorStatusReturn;
827
+ }
828
+ /**
829
+ * Single entry point for all two-factor authentication flows.
830
+ *
831
+ * The three concerns — **verify** (login), **setup** (enable) and **status**
832
+ * (manage/disable) — are distinct verbs over the same `CfgTotp*` API, so each
833
+ * is exposed as its own namespace with an independent loading/error surface:
834
+ *
835
+ * @example
836
+ * ```tsx
837
+ * const two = useTwoFactor({ verify: { redirectUrl: '/dashboard' } });
838
+ *
839
+ * await two.verify.verifyTOTP(sessionId, code); // login
840
+ * await two.setup.startSetup('My iPhone'); // enable
841
+ * await two.status.fetchStatus(); // manage
842
+ * ```
843
+ *
844
+ * Consumers that only need one concern can compose the underlying hooks
845
+ * directly (`useTwoFactorVerify` / `useTwoFactorSetup` / `useTwoFactorStatus`),
846
+ * all re-exported alongside this one, to avoid mounting the others' state.
847
+ */
848
+ declare const useTwoFactor: (options?: UseTwoFactorOptions) => UseTwoFactorReturn;
849
+
856
850
  interface AuthRedirectOptions {
857
851
  fallbackUrl?: string;
858
852
  clearOnUse?: boolean;
@@ -1111,4 +1105,4 @@ declare const Analytics: {
1111
1105
  setUser(userId: string): void;
1112
1106
  };
1113
1107
 
1114
- export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, validateEmail, validateIdentifier };
1108
+ export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
package/dist/auth.d.ts CHANGED
@@ -697,7 +697,7 @@ interface UseGithubAuthReturn {
697
697
  */
698
698
  declare const useGithubAuth: (options?: UseGithubAuthOptions) => UseGithubAuthReturn;
699
699
 
700
- interface UseTwoFactorOptions {
700
+ interface UseTwoFactorVerifyOptions {
701
701
  /** Callback on successful 2FA verification */
702
702
  onSuccess?: (user: any) => void;
703
703
  /** Callback on error */
@@ -707,7 +707,7 @@ interface UseTwoFactorOptions {
707
707
  /** Skip automatic redirect after success (caller handles navigation) */
708
708
  skipRedirect?: boolean;
709
709
  }
710
- interface UseTwoFactorReturn {
710
+ interface UseTwoFactorVerifyReturn {
711
711
  /** Loading state */
712
712
  isLoading: boolean;
713
713
  /** Error message */
@@ -726,31 +726,13 @@ interface UseTwoFactorReturn {
726
726
  clearError: () => void;
727
727
  }
728
728
  /**
729
- * Hook for 2FA verification during login.
730
- *
731
- * Usage:
732
- * 1. After OTP/OAuth verification returns requires_2fa=true and session_id
733
- * 2. Show 2FA form and collect TOTP code from user
734
- * 3. Call verifyTOTP(sessionId, code) to complete authentication
735
- *
736
- * @example
737
- * ```tsx
738
- * const { isLoading, error, verifyTOTP, verifyBackupCode } = useTwoFactor({
739
- * onSuccess: (user) => console.log('Logged in:', user),
740
- * onError: (error) => console.error(error),
741
- * redirectUrl: '/dashboard',
742
- * });
729
+ * 2FA verification during login (internal — composed by {@link useTwoFactor}).
743
730
  *
744
- * const handleSubmit = async (code: string) => {
745
- * if (useBackupCode) {
746
- * await verifyBackupCode(sessionId, code);
747
- * } else {
748
- * await verifyTOTP(sessionId, code);
749
- * }
750
- * };
751
- * ```
731
+ * After OTP/OAuth verification returns requires_2fa=true and a session_id,
732
+ * collect the TOTP (or backup) code and call verifyTOTP/verifyBackupCode to
733
+ * complete authentication.
752
734
  */
753
- declare const useTwoFactor: (options?: UseTwoFactorOptions) => UseTwoFactorReturn;
735
+ declare const useTwoFactorVerify: (options?: UseTwoFactorVerifyOptions) => UseTwoFactorVerifyReturn;
754
736
 
755
737
  interface TwoFactorSetupData {
756
738
  /** Device ID to use for confirmation */
@@ -793,38 +775,9 @@ interface UseTwoFactorSetupReturn {
793
775
  clearError: () => void;
794
776
  }
795
777
  /**
796
- * Hook for 2FA setup (enabling TOTP authentication).
797
- *
798
- * Flow:
799
- * 1. Call startSetup() to get QR code and provisioning URI
800
- * 2. User scans QR code with authenticator app
801
- * 3. Call confirmSetup(code) with the 6-digit code from app
802
- * 4. Show backup codes to user (they must save these!)
803
- *
804
- * @example
805
- * ```tsx
806
- * const {
807
- * isLoading,
808
- * error,
809
- * setupData,
810
- * backupCodes,
811
- * setupStep,
812
- * startSetup,
813
- * confirmSetup,
814
- * } = useTwoFactorSetup({
815
- * onComplete: (codes) => console.log('Backup codes:', codes),
816
- * onError: (error) => console.error(error),
817
- * });
818
- *
819
- * // Start setup
820
- * const data = await startSetup('My iPhone');
778
+ * 2FA setup enabling TOTP (internal, composed by {@link useTwoFactor}).
821
779
  *
822
- * // Show QR code
823
- * <QRCodeSVG value={data.provisioningUri} />
824
- *
825
- * // Confirm with code from app
826
- * const codes = await confirmSetup('123456');
827
- * ```
780
+ * Flow: startSetup() → user scans QR → confirmSetup(code) → show backup codes.
828
781
  */
829
782
  declare const useTwoFactorSetup: (options?: UseTwoFactorSetupOptions) => UseTwoFactorSetupReturn;
830
783
 
@@ -851,8 +804,49 @@ interface UseTwoFactorStatusReturn {
851
804
  /** Clear error */
852
805
  clearError: () => void;
853
806
  }
807
+ /**
808
+ * Read and manage 2FA status (internal, composed by {@link useTwoFactor}).
809
+ *
810
+ * Fetches the device list / enabled flag and disables 2FA.
811
+ */
854
812
  declare const useTwoFactorStatus: () => UseTwoFactorStatusReturn;
855
813
 
814
+ interface UseTwoFactorOptions {
815
+ /** Options for the login-time verification namespace */
816
+ verify?: UseTwoFactorVerifyOptions;
817
+ /** Options for the setup (enable) namespace */
818
+ setup?: UseTwoFactorSetupOptions;
819
+ }
820
+ interface UseTwoFactorReturn {
821
+ /** Verify a 2FA challenge during login (TOTP / backup code). */
822
+ verify: UseTwoFactorVerifyReturn;
823
+ /** Enable 2FA — QR provisioning → confirm → backup codes. */
824
+ setup: UseTwoFactorSetupReturn;
825
+ /** Read device list / enabled flag and disable 2FA. */
826
+ status: UseTwoFactorStatusReturn;
827
+ }
828
+ /**
829
+ * Single entry point for all two-factor authentication flows.
830
+ *
831
+ * The three concerns — **verify** (login), **setup** (enable) and **status**
832
+ * (manage/disable) — are distinct verbs over the same `CfgTotp*` API, so each
833
+ * is exposed as its own namespace with an independent loading/error surface:
834
+ *
835
+ * @example
836
+ * ```tsx
837
+ * const two = useTwoFactor({ verify: { redirectUrl: '/dashboard' } });
838
+ *
839
+ * await two.verify.verifyTOTP(sessionId, code); // login
840
+ * await two.setup.startSetup('My iPhone'); // enable
841
+ * await two.status.fetchStatus(); // manage
842
+ * ```
843
+ *
844
+ * Consumers that only need one concern can compose the underlying hooks
845
+ * directly (`useTwoFactorVerify` / `useTwoFactorSetup` / `useTwoFactorStatus`),
846
+ * all re-exported alongside this one, to avoid mounting the others' state.
847
+ */
848
+ declare const useTwoFactor: (options?: UseTwoFactorOptions) => UseTwoFactorReturn;
849
+
856
850
  interface AuthRedirectOptions {
857
851
  fallbackUrl?: string;
858
852
  clearOnUse?: boolean;
@@ -1111,4 +1105,4 @@ declare const Analytics: {
1111
1105
  setUser(userId: string): void;
1112
1106
  };
1113
1107
 
1114
- export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, validateEmail, validateIdentifier };
1108
+ export { AUTH_CONSTANTS, type AccountsContextValue, AccountsProvider, Analytics, AnalyticsCategory, type AnalyticsCategoryType, AnalyticsEvent, type AnalyticsEventType, type AuthConfig, AuthContext, type AuthContextType, type AuthFormAutoSubmit, type AuthFormReturn, type AuthFormState, type AuthFormStateHandlers, type AuthFormSubmitHandlers, type AuthFormValidation, AuthProvider, type AuthProviderProps, type AuthStep, type DeleteAccountResult, type GuardInput, type OTPRequestResult, type PatchedCfgUserUpdateRequest, PatchedCfgUserUpdateRequestSchema, type ProfileCacheOptions, type SessionSnapshot, type SessionStatus, type TwoFactorDevice, type TwoFactorSetupData, type UseAuthFormOptions, type UseAuthFormStateReturn, type UseAutoAuthOptions, type UseDeleteAccountReturn, type UseGithubAuthOptions, type UseGithubAuthReturn, type UseTwoFactorOptions, type UseTwoFactorReturn, type UseTwoFactorSetupOptions, type UseTwoFactorSetupReturn, type UseTwoFactorStatusReturn, type UseTwoFactorVerifyOptions, type UseTwoFactorVerifyReturn, type UserProfile, type WebmailLink, authLogger, clearProfileCache, consumeSavedRedirect, decodeBase64, encodeBase64, formatAuthError, getCacheMetadata, getCachedProfile, hasValidCache, isAllowedAuthPath, logger, normalizePath, peekSavedRedirect, resolveGuardIsAuthenticated, resolveGuardIsLoading, setCachedProfile, shouldRedirectToAuth, useAccountsContext, useAuth, useAuthForm, useAuthFormState, useAuthRedirectManager, useAuthValidation, useAutoAuth, useBase64, useCfgRouter, useDeleteAccount, useGithubAuth, useLocalStorage, useQueryParams, useSession, useSessionStorage, useTwoFactor, useTwoFactorSetup, useTwoFactorStatus, useTwoFactorVerify, validateEmail, validateIdentifier };
package/dist/auth.mjs CHANGED
@@ -1081,7 +1081,7 @@ var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
1081
1081
  };
1082
1082
  }, "useAutoAuth");
1083
1083
 
1084
- // src/auth/hooks/useTwoFactor.ts
1084
+ // src/auth/hooks/useTwoFactorVerify.ts
1085
1085
  import { useCallback as useCallback4, useState as useState4 } from "react";
1086
1086
 
1087
1087
  // src/_api/generated/core/bodySerializer.gen.ts
@@ -2609,8 +2609,35 @@ var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
2609
2609
  };
2610
2610
  }, "useAuthRedirectManager");
2611
2611
 
2612
- // src/auth/hooks/useTwoFactor.ts
2613
- var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
2612
+ // src/auth/hooks/twoFactor.shared.ts
2613
+ var TOTP_CODE_LENGTH = 6;
2614
+ function extractTwoFactorError(err, fallback) {
2615
+ if (err instanceof APIError) {
2616
+ const body = err.response ?? null;
2617
+ if (typeof body?.error === "string") return body.error;
2618
+ if (typeof body?.detail === "string") return body.detail;
2619
+ if (typeof body?.message === "string") return body.message;
2620
+ return err.errorMessage ?? err.message;
2621
+ }
2622
+ if (err instanceof Error) return err.message;
2623
+ return fallback;
2624
+ }
2625
+ __name(extractTwoFactorError, "extractTwoFactorError");
2626
+ function extractAttemptsRemaining(err) {
2627
+ if (err instanceof APIError) {
2628
+ const body = err.response ?? null;
2629
+ if (typeof body?.attempts_remaining === "number") return body.attempts_remaining;
2630
+ }
2631
+ return null;
2632
+ }
2633
+ __name(extractAttemptsRemaining, "extractAttemptsRemaining");
2634
+ function isValidTotpCode(code) {
2635
+ return !!code && code.length === TOTP_CODE_LENGTH;
2636
+ }
2637
+ __name(isValidTotpCode, "isValidTotpCode");
2638
+
2639
+ // src/auth/hooks/useTwoFactorVerify.ts
2640
+ var useTwoFactorVerify = /* @__PURE__ */ __name((options = {}) => {
2614
2641
  const { onSuccess, onError, redirectUrl, skipRedirect = false } = options;
2615
2642
  const router = useCfgRouter();
2616
2643
  const [isLoading, setIsLoading] = useState4(false);
@@ -2650,7 +2677,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
2650
2677
  onError?.(msg);
2651
2678
  return false;
2652
2679
  }
2653
- if (!code || code.length !== 6) {
2680
+ if (!isValidTotpCode(code)) {
2654
2681
  const msg = "Please enter a 6-digit code";
2655
2682
  setError(msg);
2656
2683
  onError?.(msg);
@@ -2672,10 +2699,9 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
2672
2699
  return true;
2673
2700
  } catch (err) {
2674
2701
  authLogger.error("2FA TOTP verification error:", err);
2675
- const errorMessage = err instanceof APIError ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid verification code";
2676
- if (err instanceof APIError && typeof err.response?.attempts_remaining === "number") {
2677
- setAttemptsRemaining(err.response.attempts_remaining);
2678
- }
2702
+ const errorMessage = extractTwoFactorError(err, "Invalid verification code");
2703
+ const attempts = extractAttemptsRemaining(err);
2704
+ if (attempts !== null) setAttemptsRemaining(attempts);
2679
2705
  setError(errorMessage);
2680
2706
  onError?.(errorMessage);
2681
2707
  Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
@@ -2719,10 +2745,9 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
2719
2745
  return true;
2720
2746
  } catch (err) {
2721
2747
  authLogger.error("2FA backup code verification error:", err);
2722
- const errorMessage = err instanceof APIError ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid backup code";
2723
- if (err instanceof APIError && typeof err.response?.attempts_remaining === "number") {
2724
- setAttemptsRemaining(err.response.attempts_remaining);
2725
- }
2748
+ const errorMessage = extractTwoFactorError(err, "Invalid backup code");
2749
+ const attempts = extractAttemptsRemaining(err);
2750
+ if (attempts !== null) setAttemptsRemaining(attempts);
2726
2751
  setError(errorMessage);
2727
2752
  onError?.(errorMessage);
2728
2753
  Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
@@ -2744,7 +2769,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
2744
2769
  verifyBackupCode,
2745
2770
  clearError
2746
2771
  };
2747
- }, "useTwoFactor");
2772
+ }, "useTwoFactorVerify");
2748
2773
 
2749
2774
  // src/auth/hooks/useAuthForm.ts
2750
2775
  var useAuthForm = /* @__PURE__ */ __name((options) => {
@@ -2788,7 +2813,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
2788
2813
  startRateLimitCountdown,
2789
2814
  setWebmail
2790
2815
  } = formState;
2791
- const twoFactor = useTwoFactor({
2816
+ const twoFactor = useTwoFactorVerify({
2792
2817
  onSuccess: /* @__PURE__ */ __name(() => {
2793
2818
  authLogger.info("2FA verification successful, showing success screen");
2794
2819
  setStep("success");
@@ -3179,7 +3204,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
3179
3204
  authLogger.info("2FA setup initiated, expires in:", data.expiresIn, "seconds");
3180
3205
  return data;
3181
3206
  } catch (err) {
3182
- const errorMessage = err instanceof Error ? err.message : "Failed to start 2FA setup";
3207
+ const errorMessage = extractTwoFactorError(err, "Failed to start 2FA setup");
3183
3208
  authLogger.error("2FA setup error:", err);
3184
3209
  setError(errorMessage);
3185
3210
  setSetupStep("idle");
@@ -3196,7 +3221,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
3196
3221
  onError?.(msg);
3197
3222
  return null;
3198
3223
  }
3199
- if (!code || code.length !== 6) {
3224
+ if (!isValidTotpCode(code)) {
3200
3225
  const msg = "Please enter a 6-digit code";
3201
3226
  setError(msg);
3202
3227
  onError?.(msg);
@@ -3220,7 +3245,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
3220
3245
  onComplete?.(codes);
3221
3246
  return codes;
3222
3247
  } catch (err) {
3223
- const errorMessage = err instanceof Error ? err.message : "Invalid code. Please try again.";
3248
+ const errorMessage = extractTwoFactorError(err, "Invalid code. Please try again.");
3224
3249
  authLogger.error("2FA setup confirmation error:", err);
3225
3250
  setError(errorMessage);
3226
3251
  setSetupStep("scanning");
@@ -3246,17 +3271,6 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
3246
3271
 
3247
3272
  // src/auth/hooks/useTwoFactorStatus.ts
3248
3273
  import { useCallback as useCallback8, useState as useState7 } from "react";
3249
- function extractErrorMessage(err, fallback) {
3250
- if (err instanceof APIError) {
3251
- const body = err.response;
3252
- if (typeof body?.error === "string") return body.error;
3253
- if (typeof body?.detail === "string") return body.detail;
3254
- return err.message;
3255
- }
3256
- if (err instanceof Error) return err.message;
3257
- return fallback;
3258
- }
3259
- __name(extractErrorMessage, "extractErrorMessage");
3260
3274
  var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
3261
3275
  const [isLoading, setIsLoading] = useState7(false);
3262
3276
  const [error, setError] = useState7(null);
@@ -3283,7 +3297,7 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
3283
3297
  setHas2FAEnabled(response.has_2fa_enabled);
3284
3298
  authLogger.info("2FA status:", response.has_2fa_enabled ? "enabled" : "disabled");
3285
3299
  } catch (err) {
3286
- const errorMessage = extractErrorMessage(err, "Failed to fetch 2FA status");
3300
+ const errorMessage = extractTwoFactorError(err, "Failed to fetch 2FA status");
3287
3301
  authLogger.error("Failed to fetch 2FA status:", err);
3288
3302
  setError(errorMessage);
3289
3303
  } finally {
@@ -3291,7 +3305,7 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
3291
3305
  }
3292
3306
  }, []);
3293
3307
  const disable2FA = useCallback8(async (code) => {
3294
- if (!code || code.length !== 6) {
3308
+ if (!isValidTotpCode(code)) {
3295
3309
  setError("Please enter a 6-digit code");
3296
3310
  return false;
3297
3311
  }
@@ -3305,7 +3319,7 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
3305
3319
  authLogger.info("2FA disabled successfully");
3306
3320
  return true;
3307
3321
  } catch (err) {
3308
- const errorMessage = extractErrorMessage(err, "Invalid verification code");
3322
+ const errorMessage = extractTwoFactorError(err, "Invalid verification code");
3309
3323
  authLogger.error("Failed to disable 2FA:", err);
3310
3324
  setError(errorMessage);
3311
3325
  return false;
@@ -3324,6 +3338,14 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
3324
3338
  };
3325
3339
  }, "useTwoFactorStatus");
3326
3340
 
3341
+ // src/auth/hooks/useTwoFactor.ts
3342
+ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
3343
+ const verify = useTwoFactorVerify(options.verify);
3344
+ const setup = useTwoFactorSetup(options.setup);
3345
+ const status = useTwoFactorStatus();
3346
+ return { verify, setup, status };
3347
+ }, "useTwoFactor");
3348
+
3327
3349
  // src/auth/hooks/useLocalStorage.ts
3328
3350
  import { useState as useState8 } from "react";
3329
3351
  function useLocalStorage(key, initialValue) {
@@ -4815,6 +4837,7 @@ export {
4815
4837
  useTwoFactor,
4816
4838
  useTwoFactorSetup,
4817
4839
  useTwoFactorStatus,
4840
+ useTwoFactorVerify,
4818
4841
  validateEmail,
4819
4842
  validateIdentifier
4820
4843
  };