@djangocfg/api 2.1.59 → 2.1.62

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/README.md CHANGED
@@ -144,6 +144,7 @@ import {
144
144
  useGithubAuth, // GitHub OAuth integration
145
145
  useTwoFactor, // 2FA verification (verify TOTP code)
146
146
  useTwoFactorSetup, // 2FA setup flow (generate QR, verify, enable)
147
+ useTwoFactorStatus, // 2FA status check and disable
147
148
  useAutoAuth, // Auto-authentication on mount
148
149
  useLocalStorage, // localStorage helper
149
150
  useSessionStorage, // sessionStorage helper
@@ -416,6 +417,46 @@ export function TwoFactorSetup() {
416
417
  }
417
418
  ```
418
419
 
420
+ ### useTwoFactorStatus
421
+
422
+ Check 2FA status and disable 2FA for authenticated users:
423
+
424
+ ```tsx
425
+ 'use client';
426
+ import { useTwoFactorStatus } from '@djangocfg/api/auth';
427
+
428
+ export function TwoFactorStatus() {
429
+ const {
430
+ // State
431
+ has2FAEnabled, // boolean | null - current 2FA status
432
+ devices, // TwoFactorDevice[] - list of TOTP devices
433
+ isLoading,
434
+ error,
435
+
436
+ // Actions
437
+ fetchStatus, // () => Promise<void> - refresh status
438
+ disable2FA, // (code: string) => Promise<boolean> - disable with TOTP code
439
+ clearError, // () => void
440
+ } = useTwoFactorStatus();
441
+
442
+ useEffect(() => {
443
+ fetchStatus();
444
+ }, [fetchStatus]);
445
+
446
+ if (has2FAEnabled) {
447
+ return (
448
+ <div>
449
+ <p>2FA is enabled</p>
450
+ <p>Devices: {devices.length}</p>
451
+ <button onClick={() => disable2FA('123456')}>Disable 2FA</button>
452
+ </div>
453
+ );
454
+ }
455
+
456
+ return <p>2FA is not enabled</p>;
457
+ }
458
+ ```
459
+
419
460
  ## Server Components & API Routes
420
461
 
421
462
  Use fetchers for server-side data fetching:
package/dist/auth.cjs CHANGED
@@ -61,6 +61,7 @@ __export(auth_exports, {
61
61
  useSessionStorage: () => useSessionStorage,
62
62
  useTwoFactor: () => useTwoFactor,
63
63
  useTwoFactorSetup: () => useTwoFactorSetup,
64
+ useTwoFactorStatus: () => useTwoFactorStatus,
64
65
  validateEmail: () => validateEmail,
65
66
  validateIdentifier: () => validateIdentifier
66
67
  });
@@ -5886,7 +5887,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
5886
5887
  sourceUrl,
5887
5888
  redirectUrl,
5888
5889
  requireTermsAcceptance = false,
5889
- authPath = "/auth"
5890
+ authPath = "/auth",
5891
+ enable2FASetup = true
5890
5892
  } = options;
5891
5893
  const formState = useAuthFormState();
5892
5894
  const validation = useAuthValidation();
@@ -6041,14 +6043,18 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
6041
6043
  }
6042
6044
  if (result.success) {
6043
6045
  saveIdentifierToStorage(submitIdentifier, submitChannel);
6044
- if (result.should_prompt_2fa) {
6046
+ if (result.should_prompt_2fa && enable2FASetup) {
6045
6047
  authLogger.info("OTP verification successful, prompting 2FA setup");
6046
6048
  setShouldPrompt2FA(true);
6047
6049
  setStep("2fa-setup");
6048
6050
  onOTPSuccess?.();
6049
6051
  return true;
6050
6052
  }
6051
- authLogger.info("OTP verification successful, showing success screen");
6053
+ if (result.should_prompt_2fa && !enable2FASetup) {
6054
+ authLogger.info("OTP verification successful, skipping 2FA setup prompt (disabled)");
6055
+ } else {
6056
+ authLogger.info("OTP verification successful, showing success screen");
6057
+ }
6052
6058
  setStep("success");
6053
6059
  onOTPSuccess?.();
6054
6060
  return true;
@@ -6065,7 +6071,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
6065
6071
  } finally {
6066
6072
  setIsLoading(false);
6067
6073
  }
6068
- }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
6074
+ }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep, enable2FASetup]);
6069
6075
  const handleOTPSubmit = (0, import_react8.useCallback)(async (e) => {
6070
6076
  e.preventDefault();
6071
6077
  await submitOTP(identifier, otp, channel);
@@ -6391,15 +6397,84 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
6391
6397
  };
6392
6398
  }, "useTwoFactorSetup");
6393
6399
 
6394
- // src/auth/hooks/useAuthGuard.ts
6400
+ // src/auth/hooks/useTwoFactorStatus.ts
6395
6401
  var import_react11 = require("react");
6402
+ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
6403
+ const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
6404
+ const [error, setError] = (0, import_react11.useState)(null);
6405
+ const [has2FAEnabled, setHas2FAEnabled] = (0, import_react11.useState)(null);
6406
+ const [devices, setDevices] = (0, import_react11.useState)([]);
6407
+ const clearError = (0, import_react11.useCallback)(() => {
6408
+ setError(null);
6409
+ }, []);
6410
+ const fetchStatus = (0, import_react11.useCallback)(async () => {
6411
+ setIsLoading(true);
6412
+ setError(null);
6413
+ try {
6414
+ authLogger.info("Fetching 2FA status...");
6415
+ const response = await apiTotp.totp_management.totpDevicesList();
6416
+ const mappedDevices = (response.results || []).map((device) => ({
6417
+ id: device.id,
6418
+ name: device.name,
6419
+ createdAt: device.created_at,
6420
+ lastUsedAt: device.last_used_at ?? null,
6421
+ isPrimary: device.is_primary
6422
+ }));
6423
+ setDevices(mappedDevices);
6424
+ const enabled = mappedDevices.length > 0;
6425
+ setHas2FAEnabled(enabled);
6426
+ authLogger.info("2FA status:", enabled ? "enabled" : "disabled");
6427
+ } catch (err) {
6428
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch 2FA status";
6429
+ authLogger.error("Failed to fetch 2FA status:", err);
6430
+ setError(errorMessage);
6431
+ } finally {
6432
+ setIsLoading(false);
6433
+ }
6434
+ }, []);
6435
+ const disable2FA = (0, import_react11.useCallback)(async (code) => {
6436
+ if (!code || code.length !== 6) {
6437
+ setError("Please enter a 6-digit code");
6438
+ return false;
6439
+ }
6440
+ setIsLoading(true);
6441
+ setError(null);
6442
+ try {
6443
+ authLogger.info("Disabling 2FA...");
6444
+ await apiTotp.totp_management.totpDisableCreate({ code });
6445
+ setHas2FAEnabled(false);
6446
+ setDevices([]);
6447
+ authLogger.info("2FA disabled successfully");
6448
+ return true;
6449
+ } catch (err) {
6450
+ const errorMessage = err instanceof Error ? err.message : "Invalid verification code";
6451
+ authLogger.error("Failed to disable 2FA:", err);
6452
+ setError(errorMessage);
6453
+ return false;
6454
+ } finally {
6455
+ setIsLoading(false);
6456
+ }
6457
+ }, []);
6458
+ return {
6459
+ isLoading,
6460
+ error,
6461
+ has2FAEnabled,
6462
+ devices,
6463
+ fetchStatus,
6464
+ disable2FA,
6465
+ clearError
6466
+ };
6467
+ }, "useTwoFactorStatus");
6468
+
6469
+ // src/auth/hooks/useAuthGuard.ts
6470
+ var import_react12 = require("react");
6396
6471
  var import_hooks6 = require("@djangocfg/ui-nextjs/hooks");
6397
6472
  var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
6398
6473
  const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
6399
6474
  const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
6400
6475
  const router = (0, import_hooks6.useCfgRouter)();
6401
- const [isRedirecting, setIsRedirecting] = (0, import_react11.useState)(false);
6402
- (0, import_react11.useEffect)(() => {
6476
+ const [isRedirecting, setIsRedirecting] = (0, import_react12.useState)(false);
6477
+ (0, import_react12.useEffect)(() => {
6403
6478
  if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
6404
6479
  if (shouldSaveUrl && typeof window !== "undefined") {
6405
6480
  const currentUrl = window.location.pathname + window.location.search;
@@ -6413,9 +6488,9 @@ var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
6413
6488
  }, "useAuthGuard");
6414
6489
 
6415
6490
  // src/auth/hooks/useLocalStorage.ts
6416
- var import_react12 = require("react");
6491
+ var import_react13 = require("react");
6417
6492
  function useLocalStorage2(key, initialValue) {
6418
- const [storedValue, setStoredValue] = (0, import_react12.useState)(() => {
6493
+ const [storedValue, setStoredValue] = (0, import_react13.useState)(() => {
6419
6494
  if (typeof window === "undefined") {
6420
6495
  return initialValue;
6421
6496
  }