@djangocfg/api 2.1.57 → 2.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.
Files changed (75) hide show
  1. package/README.md +125 -9
  2. package/dist/auth.cjs +1865 -402
  3. package/dist/auth.cjs.map +1 -1
  4. package/dist/auth.d.cts +352 -76
  5. package/dist/auth.d.ts +352 -76
  6. package/dist/auth.mjs +1867 -404
  7. package/dist/auth.mjs.map +1 -1
  8. package/dist/clients.cjs +1637 -137
  9. package/dist/clients.cjs.map +1 -1
  10. package/dist/clients.d.cts +1394 -282
  11. package/dist/clients.d.ts +1394 -282
  12. package/dist/clients.mjs +1637 -137
  13. package/dist/clients.mjs.map +1 -1
  14. package/dist/hooks.cjs +24 -11
  15. package/dist/hooks.cjs.map +1 -1
  16. package/dist/hooks.d.cts +88 -21
  17. package/dist/hooks.d.ts +88 -21
  18. package/dist/hooks.mjs +24 -11
  19. package/dist/hooks.mjs.map +1 -1
  20. package/dist/index.cjs +38 -17
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +94 -21
  23. package/dist/index.d.ts +94 -21
  24. package/dist/index.mjs +38 -17
  25. package/dist/index.mjs.map +1 -1
  26. package/package.json +3 -3
  27. package/src/auth/context/AccountsContext.tsx +8 -1
  28. package/src/auth/context/AuthContext.tsx +31 -8
  29. package/src/auth/context/types.ts +8 -1
  30. package/src/auth/hooks/index.ts +29 -5
  31. package/src/auth/hooks/useAuthForm.ts +292 -226
  32. package/src/auth/hooks/useAuthFormState.ts +60 -0
  33. package/src/auth/hooks/useAuthValidation.ts +77 -0
  34. package/src/auth/hooks/useGithubAuth.ts +26 -5
  35. package/src/auth/hooks/useTwoFactor.ts +239 -0
  36. package/src/auth/hooks/useTwoFactorSetup.ts +213 -0
  37. package/src/auth/index.ts +3 -0
  38. package/src/auth/types/form.ts +194 -0
  39. package/src/auth/types/index.ts +28 -0
  40. package/src/clients.ts +10 -0
  41. package/src/generated/cfg_accounts/_utils/schemas/OAuthTokenResponse.schema.ts +26 -3
  42. package/src/generated/cfg_accounts/_utils/schemas/OTPVerifyResponse.schema.ts +26 -3
  43. package/src/generated/cfg_accounts/accounts/client.ts +4 -1
  44. package/src/generated/cfg_accounts/accounts/models.ts +15 -6
  45. package/src/generated/cfg_accounts/accounts__oauth/models.ts +16 -7
  46. package/src/generated/cfg_accounts/client.ts +5 -2
  47. package/src/generated/cfg_accounts/http.ts +8 -2
  48. package/src/generated/cfg_accounts/schema.json +47 -19
  49. package/src/generated/cfg_centrifugo/client.ts +5 -2
  50. package/src/generated/cfg_centrifugo/http.ts +8 -2
  51. package/src/generated/cfg_totp/CLAUDE.md +12 -12
  52. package/src/generated/cfg_totp/_utils/fetchers/index.ts +3 -3
  53. package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_management.ts → totp__totp_management.ts} +3 -3
  54. package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_setup.ts → totp__totp_setup.ts} +3 -3
  55. package/src/generated/cfg_totp/_utils/fetchers/{totp__2fa_verification.ts → totp__totp_verification.ts} +3 -3
  56. package/src/generated/cfg_totp/_utils/hooks/index.ts +3 -3
  57. package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_management.ts → totp__totp_management.ts} +2 -2
  58. package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_setup.ts → totp__totp_setup.ts} +2 -2
  59. package/src/generated/cfg_totp/_utils/hooks/{totp__2fa_verification.ts → totp__totp_verification.ts} +2 -2
  60. package/src/generated/cfg_totp/_utils/schemas/DeviceList.schema.ts +1 -1
  61. package/src/generated/cfg_totp/client.ts +14 -11
  62. package/src/generated/cfg_totp/http.ts +8 -2
  63. package/src/generated/cfg_totp/index.ts +16 -16
  64. package/src/generated/cfg_totp/schema.json +8 -7
  65. package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/client.ts +2 -2
  66. package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/models.ts +1 -1
  67. package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/client.ts +4 -4
  68. package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/client.ts +2 -2
  69. package/src/generated/cfg_webpush/client.ts +5 -2
  70. package/src/generated/cfg_webpush/http.ts +8 -2
  71. /package/src/generated/cfg_totp/{totp__2fa_management → totp__totp_management}/index.ts +0 -0
  72. /package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/index.ts +0 -0
  73. /package/src/generated/cfg_totp/{totp__2fa_setup → totp__totp_setup}/models.ts +0 -0
  74. /package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/index.ts +0 -0
  75. /package/src/generated/cfg_totp/{totp__2fa_verification → totp__totp_verification}/models.ts +0 -0
package/dist/auth.mjs CHANGED
@@ -175,7 +175,10 @@ var Accounts = class {
175
175
  return response;
176
176
  }
177
177
  /**
178
- * Verify OTP code and return JWT tokens.
178
+ * Verify OTP code and return JWT tokens or 2FA session. If user has 2FA
179
+ * enabled: - Returns requires_2fa=True with session_id - Client must
180
+ * complete 2FA verification at /cfg/totp/verify/ If user has no 2FA: -
181
+ * Returns JWT tokens and user data directly
179
182
  */
180
183
  async otpVerifyCreate(data) {
181
184
  const response = await this.client.request("POST", "/cfg/accounts/otp/verify/", { body: data });
@@ -189,7 +192,7 @@ var FetchAdapter = class {
189
192
  __name(this, "FetchAdapter");
190
193
  }
191
194
  async request(request) {
192
- const { method, url, headers, body, params, formData } = request;
195
+ const { method, url, headers, body, params, formData, binaryBody } = request;
193
196
  let finalUrl = url;
194
197
  if (params) {
195
198
  const searchParams = new URLSearchParams();
@@ -207,6 +210,9 @@ var FetchAdapter = class {
207
210
  let requestBody;
208
211
  if (formData) {
209
212
  requestBody = formData;
213
+ } else if (binaryBody) {
214
+ finalHeaders["Content-Type"] = "application/octet-stream";
215
+ requestBody = binaryBody;
210
216
  } else if (body) {
211
217
  finalHeaders["Content-Type"] = "application/json";
212
218
  requestBody = JSON.stringify(body);
@@ -606,7 +612,7 @@ var APIClient = class {
606
612
  const headers = {
607
613
  ...options?.headers || {}
608
614
  };
609
- if (!options?.formData && !headers["Content-Type"]) {
615
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
610
616
  headers["Content-Type"] = "application/json";
611
617
  }
612
618
  if (this.logger) {
@@ -625,7 +631,8 @@ var APIClient = class {
625
631
  headers,
626
632
  params: options?.params,
627
633
  body: options?.body,
628
- formData: options?.formData
634
+ formData: options?.formData,
635
+ binaryBody: options?.binaryBody
629
636
  });
630
637
  const duration = Date.now() - startTime;
631
638
  if (response.status >= 400) {
@@ -879,11 +886,14 @@ var OAuthProvidersResponseSchema = z8.object({
879
886
  // src/generated/cfg_accounts/_utils/schemas/OAuthTokenResponse.schema.ts
880
887
  import { z as z9 } from "zod";
881
888
  var OAuthTokenResponseSchema = z9.object({
882
- access: z9.string(),
883
- refresh: z9.string(),
884
- user: z9.record(z9.string(), z9.any()),
889
+ requires_2fa: z9.boolean().optional(),
890
+ session_id: z9.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).nullable().optional(),
891
+ access: z9.string().nullable().optional(),
892
+ refresh: z9.string().nullable().optional(),
893
+ user: z9.record(z9.string(), z9.any()).nullable().optional(),
885
894
  is_new_user: z9.boolean(),
886
- is_new_connection: z9.boolean()
895
+ is_new_connection: z9.boolean(),
896
+ should_prompt_2fa: z9.boolean().optional()
887
897
  });
888
898
 
889
899
  // src/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts
@@ -942,9 +952,12 @@ var UserSchema = z14.object({
942
952
 
943
953
  // src/generated/cfg_accounts/_utils/schemas/OTPVerifyResponse.schema.ts
944
954
  var OTPVerifyResponseSchema = z15.object({
945
- refresh: z15.string(),
946
- access: z15.string(),
947
- user: UserSchema
955
+ requires_2fa: z15.boolean().optional(),
956
+ session_id: z15.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).nullable().optional(),
957
+ refresh: z15.string().nullable().optional(),
958
+ access: z15.string().nullable().optional(),
959
+ user: UserSchema.nullable().optional(),
960
+ should_prompt_2fa: z15.boolean().optional()
948
961
  });
949
962
 
950
963
  // src/generated/cfg_accounts/_utils/schemas/PatchedUserProfileUpdateRequest.schema.ts
@@ -1663,7 +1676,7 @@ var FetchAdapter2 = class {
1663
1676
  __name(this, "FetchAdapter");
1664
1677
  }
1665
1678
  async request(request) {
1666
- const { method, url, headers, body, params, formData } = request;
1679
+ const { method, url, headers, body, params, formData, binaryBody } = request;
1667
1680
  let finalUrl = url;
1668
1681
  if (params) {
1669
1682
  const searchParams = new URLSearchParams();
@@ -1681,6 +1694,9 @@ var FetchAdapter2 = class {
1681
1694
  let requestBody;
1682
1695
  if (formData) {
1683
1696
  requestBody = formData;
1697
+ } else if (binaryBody) {
1698
+ finalHeaders["Content-Type"] = "application/octet-stream";
1699
+ requestBody = binaryBody;
1684
1700
  } else if (body) {
1685
1701
  finalHeaders["Content-Type"] = "application/json";
1686
1702
  requestBody = JSON.stringify(body);
@@ -2080,7 +2096,7 @@ var APIClient2 = class {
2080
2096
  const headers = {
2081
2097
  ...options?.headers || {}
2082
2098
  };
2083
- if (!options?.formData && !headers["Content-Type"]) {
2099
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
2084
2100
  headers["Content-Type"] = "application/json";
2085
2101
  }
2086
2102
  if (this.logger) {
@@ -2099,7 +2115,8 @@ var APIClient2 = class {
2099
2115
  headers,
2100
2116
  params: options?.params,
2101
2117
  body: options?.body,
2102
- formData: options?.formData
2118
+ formData: options?.formData,
2119
+ binaryBody: options?.binaryBody
2103
2120
  });
2104
2121
  const duration = Date.now() - startTime;
2105
2122
  if (response.status >= 400) {
@@ -2760,7 +2777,7 @@ var FetchAdapter3 = class {
2760
2777
  __name(this, "FetchAdapter");
2761
2778
  }
2762
2779
  async request(request) {
2763
- const { method, url, headers, body, params, formData } = request;
2780
+ const { method, url, headers, body, params, formData, binaryBody } = request;
2764
2781
  let finalUrl = url;
2765
2782
  if (params) {
2766
2783
  const searchParams = new URLSearchParams();
@@ -2778,6 +2795,9 @@ var FetchAdapter3 = class {
2778
2795
  let requestBody;
2779
2796
  if (formData) {
2780
2797
  requestBody = formData;
2798
+ } else if (binaryBody) {
2799
+ finalHeaders["Content-Type"] = "application/octet-stream";
2800
+ requestBody = binaryBody;
2781
2801
  } else if (body) {
2782
2802
  finalHeaders["Content-Type"] = "application/json";
2783
2803
  requestBody = JSON.stringify(body);
@@ -3174,7 +3194,7 @@ var APIClient3 = class {
3174
3194
  const headers = {
3175
3195
  ...options?.headers || {}
3176
3196
  };
3177
- if (!options?.formData && !headers["Content-Type"]) {
3197
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
3178
3198
  headers["Content-Type"] = "application/json";
3179
3199
  }
3180
3200
  if (this.logger) {
@@ -3193,7 +3213,8 @@ var APIClient3 = class {
3193
3213
  headers,
3194
3214
  params: options?.params,
3195
3215
  body: options?.body,
3196
- formData: options?.formData
3216
+ formData: options?.formData,
3217
+ binaryBody: options?.binaryBody
3197
3218
  });
3198
3219
  const duration = Date.now() - startTime;
3199
3220
  if (response.status >= 400) {
@@ -4070,6 +4091,10 @@ function AccountsProvider({ children }) {
4070
4091
  }, "requestOTP");
4071
4092
  const verifyOTP = /* @__PURE__ */ __name(async (data) => {
4072
4093
  const result = await otpVerifyMutation(data, api);
4094
+ if (result.requires_2fa && result.session_id) {
4095
+ authLogger.info("2FA required, session:", result.session_id);
4096
+ return result;
4097
+ }
4073
4098
  if (result.access && result.refresh) {
4074
4099
  api.setToken(result.access, result.refresh);
4075
4100
  await refreshProfile("AccountsContext.verifyOTP");
@@ -4328,7 +4353,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4328
4353
  [accounts]
4329
4354
  );
4330
4355
  const verifyOTP = useCallback2(
4331
- async (identifier, otpCode, channel, sourceUrl, redirectUrl) => {
4356
+ async (identifier, otpCode, channel, sourceUrl, redirectUrl, skipRedirect) => {
4332
4357
  try {
4333
4358
  const channelValue = channel === "phone" ? enums_exports.OTPVerifyRequestChannel.PHONE : enums_exports.OTPVerifyRequestChannel.EMAIL;
4334
4359
  const result = await accounts.verifyOTP({
@@ -4336,6 +4361,16 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4336
4361
  otp: otpCode,
4337
4362
  channel: channelValue
4338
4363
  });
4364
+ if (result.requires_2fa && result.session_id) {
4365
+ authLogger.info("2FA required, session:", result.session_id);
4366
+ return {
4367
+ success: true,
4368
+ message: "OTP verified, 2FA required",
4369
+ requires_2fa: true,
4370
+ session_id: result.session_id,
4371
+ should_prompt_2fa: result.should_prompt_2fa
4372
+ };
4373
+ }
4339
4374
  if (!result.access || !result.refresh) {
4340
4375
  authLogger.error("Verify OTP returned invalid response:", result);
4341
4376
  return {
@@ -4358,14 +4393,17 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4358
4393
  if (result.user?.id) {
4359
4394
  Analytics.setUser(String(result.user.id));
4360
4395
  }
4361
- const savedRedirect = redirectManager.useAndClearRedirect();
4362
- const finalRedirectUrl = redirectUrl || savedRedirect || config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
4363
- authLogger.info("Redirecting after auth to:", finalRedirectUrl);
4364
- router.hardPush(finalRedirectUrl);
4396
+ if (!skipRedirect) {
4397
+ const savedRedirect = redirectManager.useAndClearRedirect();
4398
+ const finalRedirectUrl = redirectUrl || savedRedirect || config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
4399
+ authLogger.info("Redirecting after auth to:", finalRedirectUrl);
4400
+ router.hardPush(finalRedirectUrl);
4401
+ }
4365
4402
  return {
4366
4403
  success: true,
4367
4404
  message: "Login successful",
4368
- user: result.user
4405
+ user: result.user,
4406
+ should_prompt_2fa: result.should_prompt_2fa
4369
4407
  };
4370
4408
  } catch (error) {
4371
4409
  authLogger.error("Verify OTP error:", error);
@@ -4505,203 +4543,111 @@ var useAuth = /* @__PURE__ */ __name(() => {
4505
4543
  return context;
4506
4544
  }, "useAuth");
4507
4545
 
4508
- // src/auth/hooks/useAuthGuard.ts
4509
- import { useEffect as useEffect3, useState as useState4 } from "react";
4510
- import { useCfgRouter as useCfgRouter2 } from "@djangocfg/ui-nextjs/hooks";
4511
- var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
4512
- const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
4513
- const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
4514
- const router = useCfgRouter2();
4515
- const [isRedirecting, setIsRedirecting] = useState4(false);
4516
- useEffect3(() => {
4517
- if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
4518
- if (shouldSaveUrl && typeof window !== "undefined") {
4519
- const currentUrl = window.location.pathname + window.location.search;
4520
- saveRedirectUrl(currentUrl);
4521
- }
4522
- setIsRedirecting(true);
4523
- router.push(redirectTo);
4524
- }
4525
- }, [isAuthenticated, isLoading, router, redirectTo, requireAuth, isRedirecting, shouldSaveUrl, saveRedirectUrl]);
4526
- return { isAuthenticated, isLoading, isRedirecting };
4527
- }, "useAuthGuard");
4528
-
4529
- // src/auth/hooks/useLocalStorage.ts
4530
- import { useState as useState5 } from "react";
4531
- function useLocalStorage2(key, initialValue) {
4532
- const [storedValue, setStoredValue] = useState5(() => {
4533
- if (typeof window === "undefined") {
4534
- return initialValue;
4535
- }
4536
- try {
4537
- const item = window.localStorage.getItem(key);
4538
- if (item === null) {
4539
- return initialValue;
4540
- }
4541
- try {
4542
- return JSON.parse(item);
4543
- } catch {
4544
- return item;
4545
- }
4546
- } catch (error) {
4547
- authLogger.error(`Error reading localStorage key "${key}":`, error);
4548
- return initialValue;
4549
- }
4550
- });
4551
- const checkDataSize = /* @__PURE__ */ __name((data) => {
4552
- try {
4553
- const jsonString = JSON.stringify(data);
4554
- const sizeInBytes = new Blob([jsonString]).size;
4555
- const sizeInKB = sizeInBytes / 1024;
4556
- if (sizeInKB > 1024) {
4557
- authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
4558
- return false;
4559
- }
4560
- return true;
4561
- } catch (error) {
4562
- authLogger.error(`Error checking data size for key "${key}":`, error);
4563
- return false;
4564
- }
4565
- }, "checkDataSize");
4566
- const clearOldData = /* @__PURE__ */ __name(() => {
4567
- try {
4568
- const keys = Object.keys(localStorage).filter((key2) => key2 && typeof key2 === "string");
4569
- if (keys.length > 50) {
4570
- const itemsToRemove = Math.ceil(keys.length * 0.2);
4571
- for (let i = 0; i < itemsToRemove; i++) {
4572
- try {
4573
- const key2 = keys[i];
4574
- if (key2) {
4575
- localStorage.removeItem(key2);
4576
- localStorage.removeItem(`${key2}_timestamp`);
4577
- }
4578
- } catch {
4579
- }
4580
- }
4581
- }
4582
- } catch (error) {
4583
- authLogger.error("Error clearing old localStorage data:", error);
4546
+ // src/auth/hooks/useAuthFormState.ts
4547
+ import { useCallback as useCallback3, useState as useState4 } from "react";
4548
+ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialChannel = "email") => {
4549
+ const [identifier, setIdentifier] = useState4(initialIdentifier);
4550
+ const [channel, setChannel] = useState4(initialChannel);
4551
+ const [otp, setOtp] = useState4("");
4552
+ const [isLoading, setIsLoading] = useState4(false);
4553
+ const [acceptedTerms, setAcceptedTerms] = useState4(false);
4554
+ const [step, setStep] = useState4("identifier");
4555
+ const [error, setError] = useState4("");
4556
+ const [twoFactorSessionId, setTwoFactorSessionId] = useState4(null);
4557
+ const [shouldPrompt2FA, setShouldPrompt2FA] = useState4(false);
4558
+ const [twoFactorCode, setTwoFactorCode] = useState4("");
4559
+ const [useBackupCode, setUseBackupCode] = useState4(false);
4560
+ const clearError = useCallback3(() => setError(""), []);
4561
+ return {
4562
+ // State
4563
+ identifier,
4564
+ channel,
4565
+ otp,
4566
+ isLoading,
4567
+ acceptedTerms,
4568
+ step,
4569
+ error,
4570
+ twoFactorSessionId,
4571
+ shouldPrompt2FA,
4572
+ twoFactorCode,
4573
+ useBackupCode,
4574
+ // Handlers
4575
+ setIdentifier,
4576
+ setChannel,
4577
+ setOtp,
4578
+ setAcceptedTerms,
4579
+ setError,
4580
+ clearError,
4581
+ setStep,
4582
+ setIsLoading,
4583
+ setTwoFactorSessionId,
4584
+ setShouldPrompt2FA,
4585
+ setTwoFactorCode,
4586
+ setUseBackupCode
4587
+ };
4588
+ }, "useAuthFormState");
4589
+
4590
+ // src/auth/hooks/useAuthValidation.ts
4591
+ import { useCallback as useCallback4 } from "react";
4592
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4593
+ var PHONE_REGEX = /^\+[1-9]\d{6,14}$/;
4594
+ var useAuthValidation = /* @__PURE__ */ __name(() => {
4595
+ const detectChannelFromIdentifier2 = useCallback4((id) => {
4596
+ if (!id) return null;
4597
+ if (id.includes("@")) {
4598
+ return "email";
4584
4599
  }
4585
- }, "clearOldData");
4586
- const forceClearAll = /* @__PURE__ */ __name(() => {
4587
- try {
4588
- const keys = Object.keys(localStorage);
4589
- for (const key2 of keys) {
4590
- try {
4591
- localStorage.removeItem(key2);
4592
- } catch {
4593
- }
4594
- }
4595
- } catch (error) {
4596
- authLogger.error("Error force clearing localStorage:", error);
4600
+ if (id.startsWith("+") && PHONE_REGEX.test(id)) {
4601
+ return "phone";
4597
4602
  }
4598
- }, "forceClearAll");
4599
- const setValue = /* @__PURE__ */ __name((value) => {
4600
- try {
4601
- const valueToStore = value instanceof Function ? value(storedValue) : value;
4602
- if (!checkDataSize(valueToStore)) {
4603
- authLogger.warn(`Data size too large for key "${key}", removing key`);
4604
- try {
4605
- window.localStorage.removeItem(key);
4606
- window.localStorage.removeItem(`${key}_timestamp`);
4607
- } catch {
4608
- }
4609
- setStoredValue(valueToStore);
4610
- return;
4611
- }
4612
- setStoredValue(valueToStore);
4613
- if (typeof window !== "undefined") {
4614
- try {
4615
- if (typeof valueToStore === "string") {
4616
- window.localStorage.setItem(key, valueToStore);
4617
- } else {
4618
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4619
- }
4620
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4621
- } catch (storageError) {
4622
- if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
4623
- authLogger.warn("localStorage quota exceeded, clearing old data...");
4624
- clearOldData();
4625
- try {
4626
- if (typeof valueToStore === "string") {
4627
- window.localStorage.setItem(key, valueToStore);
4628
- } else {
4629
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4630
- }
4631
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4632
- } catch (retryError) {
4633
- authLogger.error(`Failed to set localStorage key "${key}" after clearing old data:`, retryError);
4634
- try {
4635
- forceClearAll();
4636
- if (typeof valueToStore === "string") {
4637
- window.localStorage.setItem(key, valueToStore);
4638
- } else {
4639
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4640
- }
4641
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4642
- } catch (finalError) {
4643
- authLogger.error(`Failed to set localStorage key "${key}" after force clearing:`, finalError);
4644
- setStoredValue(valueToStore);
4645
- }
4646
- }
4647
- } else {
4648
- throw storageError;
4649
- }
4650
- }
4651
- }
4652
- } catch (error) {
4653
- authLogger.error(`Error setting localStorage key "${key}":`, error);
4654
- const valueToStore = value instanceof Function ? value(storedValue) : value;
4655
- setStoredValue(valueToStore);
4603
+ return null;
4604
+ }, []);
4605
+ const validateIdentifier2 = useCallback4((id, channelType) => {
4606
+ if (!id) return false;
4607
+ const channel = channelType || detectChannelFromIdentifier2(id);
4608
+ if (channel === "email") {
4609
+ return EMAIL_REGEX.test(id);
4656
4610
  }
4657
- }, "setValue");
4658
- const removeValue = /* @__PURE__ */ __name(() => {
4659
- try {
4660
- setStoredValue(initialValue);
4661
- if (typeof window !== "undefined") {
4662
- try {
4663
- window.localStorage.removeItem(key);
4664
- window.localStorage.removeItem(`${key}_timestamp`);
4665
- } catch (removeError) {
4666
- if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
4667
- authLogger.warn("localStorage quota exceeded during removal, clearing old data...");
4668
- clearOldData();
4669
- try {
4670
- window.localStorage.removeItem(key);
4671
- window.localStorage.removeItem(`${key}_timestamp`);
4672
- } catch (retryError) {
4673
- authLogger.error(`Failed to remove localStorage key "${key}" after clearing:`, retryError);
4674
- forceClearAll();
4675
- }
4676
- } else {
4677
- throw removeError;
4678
- }
4679
- }
4680
- }
4681
- } catch (error) {
4682
- authLogger.error(`Error removing localStorage key "${key}":`, error);
4611
+ if (channel === "phone") {
4612
+ return PHONE_REGEX.test(id);
4683
4613
  }
4684
- }, "removeValue");
4685
- return [storedValue, setValue, removeValue];
4686
- }
4687
- __name(useLocalStorage2, "useLocalStorage");
4614
+ return false;
4615
+ }, [detectChannelFromIdentifier2]);
4616
+ return {
4617
+ detectChannelFromIdentifier: detectChannelFromIdentifier2,
4618
+ validateIdentifier: validateIdentifier2
4619
+ };
4620
+ }, "useAuthValidation");
4621
+ var detectChannelFromIdentifier = /* @__PURE__ */ __name((id) => {
4622
+ if (!id) return null;
4623
+ if (id.includes("@")) return "email";
4624
+ if (id.startsWith("+") && PHONE_REGEX.test(id)) return "phone";
4625
+ return null;
4626
+ }, "detectChannelFromIdentifier");
4627
+ var validateIdentifier = /* @__PURE__ */ __name((id, channelType) => {
4628
+ if (!id) return false;
4629
+ const channel = channelType || detectChannelFromIdentifier(id);
4630
+ if (channel === "email") return EMAIL_REGEX.test(id);
4631
+ if (channel === "phone") return PHONE_REGEX.test(id);
4632
+ return false;
4633
+ }, "validateIdentifier");
4688
4634
 
4689
4635
  // src/auth/hooks/useAuthForm.ts
4690
- import { useCallback as useCallback3, useEffect as useEffect6, useState as useState6 } from "react";
4636
+ import { useCallback as useCallback6, useEffect as useEffect4, useRef as useRef3 } from "react";
4691
4637
 
4692
4638
  // src/auth/hooks/useAutoAuth.ts
4693
4639
  import { usePathname as usePathname2 } from "next/navigation";
4694
- import { useEffect as useEffect5 } from "react";
4695
- import { useCfgRouter as useCfgRouter3, useQueryParams as useQueryParams2 } from "@djangocfg/ui-nextjs/hooks";
4640
+ import { useEffect as useEffect3 } from "react";
4641
+ import { useCfgRouter as useCfgRouter2, useQueryParams as useQueryParams2 } from "@djangocfg/ui-nextjs/hooks";
4696
4642
  var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
4697
4643
  const { onOTPDetected, cleanupUrl = true, allowedPaths = ["/auth"] } = options;
4698
4644
  const queryParams = useQueryParams2();
4699
4645
  const pathname = usePathname2();
4700
- const router = useCfgRouter3();
4646
+ const router = useCfgRouter2();
4701
4647
  const isAllowedPath = allowedPaths.some((path) => pathname === path || pathname?.startsWith(path + "/"));
4702
4648
  const hasOTP = !!queryParams.get("otp");
4703
4649
  const isReady = !!pathname && hasOTP && isAllowedPath;
4704
- useEffect5(() => {
4650
+ useEffect3(() => {
4705
4651
  if (!isReady) return;
4706
4652
  const queryOtp = queryParams.get("otp");
4707
4653
  if (queryOtp && typeof queryOtp === "string" && queryOtp.length === 6) {
@@ -4722,81 +4668,1275 @@ var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
4722
4668
  };
4723
4669
  }, "useAutoAuth");
4724
4670
 
4725
- // src/auth/hooks/useAuthForm.ts
4726
- var useAuthForm = /* @__PURE__ */ __name((options) => {
4727
- const { onIdentifierSuccess, onOTPSuccess, onError, sourceUrl, redirectUrl, requireTermsAcceptance = false, authPath = "/auth" } = options;
4728
- const [identifier, setIdentifier] = useState6("");
4729
- const [channel, setChannel] = useState6("email");
4730
- const [otp, setOtp] = useState6("");
4731
- const [isLoading, setIsLoading] = useState6(false);
4732
- const [acceptedTerms, setAcceptedTerms] = useState6(false);
4733
- const [step, setStep] = useState6("identifier");
4734
- const [error, setError] = useState6("");
4735
- const { requestOTP, verifyOTP, getSavedEmail, saveEmail, getSavedPhone, savePhone } = useAuth();
4736
- const [savedTermsAccepted, setSavedTermsAccepted] = useLocalStorage2("auth_terms_accepted", false);
4737
- const [savedEmail, setSavedEmail] = useLocalStorage2("auth_email", "");
4738
- const [savedPhone, setSavedPhone] = useLocalStorage2("auth_phone", "");
4739
- const detectChannelFromIdentifier = useCallback3((identifier2) => {
4740
- if (!identifier2) return null;
4741
- if (identifier2.includes("@")) {
4742
- return "email";
4743
- }
4744
- if (identifier2.startsWith("+") && /^\+[1-9]\d{6,14}$/.test(identifier2)) {
4745
- return "phone";
4746
- }
4747
- return null;
4748
- }, []);
4749
- const validateIdentifier = useCallback3((identifier2, channelType) => {
4750
- if (!identifier2) return false;
4751
- const detectedChannel = channelType || detectChannelFromIdentifier(identifier2);
4752
- if (detectedChannel === "email") {
4753
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(identifier2);
4754
- } else if (detectedChannel === "phone") {
4755
- return /^\+[1-9]\d{6,14}$/.test(identifier2);
4756
- }
4757
- return false;
4758
- }, [detectChannelFromIdentifier]);
4759
- useEffect6(() => {
4760
- const authSavedEmail = getSavedEmail();
4761
- const authSavedPhone = getSavedPhone();
4762
- if (authSavedPhone) {
4763
- setIdentifier(authSavedPhone);
4764
- setChannel("phone");
4765
- } else if (authSavedEmail) {
4766
- setIdentifier(authSavedEmail);
4767
- setChannel("email");
4768
- }
4769
- if (savedTermsAccepted) {
4770
- setAcceptedTerms(savedTermsAccepted);
4771
- }
4772
- }, [getSavedEmail, getSavedPhone, savedTermsAccepted]);
4773
- useEffect6(() => {
4774
- if (identifier) {
4775
- const detectedChannel = detectChannelFromIdentifier(identifier);
4776
- if (detectedChannel && detectedChannel !== channel) {
4777
- setChannel(detectedChannel);
4778
- }
4779
- }
4780
- }, [identifier, channel, detectChannelFromIdentifier]);
4781
- const clearError = useCallback3(() => setError(""), []);
4782
- const handleIdentifierSubmit = useCallback3(async (e) => {
4783
- e.preventDefault();
4784
- if (!identifier) {
4785
- const message = channel === "phone" ? "Please enter your phone number" : "Please enter your email address";
4786
- setError(message);
4787
- onError?.(message);
4788
- return;
4789
- }
4790
- if (!validateIdentifier(identifier, channel)) {
4791
- const message = channel === "phone" ? "Please enter a valid phone number (e.g., +1234567890)" : "Please enter a valid email address";
4792
- setError(message);
4793
- onError?.(message);
4794
- return;
4671
+ // src/auth/hooks/useTwoFactor.ts
4672
+ import { useCallback as useCallback5, useState as useState5 } from "react";
4673
+ import { useCfgRouter as useCfgRouter3 } from "@djangocfg/ui-nextjs/hooks";
4674
+
4675
+ // src/generated/cfg_totp/totp__backup_codes/client.ts
4676
+ var BackupCodes = class {
4677
+ static {
4678
+ __name(this, "BackupCodes");
4679
+ }
4680
+ constructor(client) {
4681
+ this.client = client;
4682
+ }
4683
+ /**
4684
+ * Get backup codes status for user.
4685
+ */
4686
+ async totpBackupCodesRetrieve() {
4687
+ const response = await this.client.request("GET", "/cfg/totp/backup-codes/");
4688
+ return response;
4689
+ }
4690
+ /**
4691
+ * Regenerate backup codes. Requires TOTP code for verification.
4692
+ * Invalidates all existing codes.
4693
+ */
4694
+ async totpBackupCodesRegenerateCreate(data) {
4695
+ const response = await this.client.request("POST", "/cfg/totp/backup-codes/regenerate/", { body: data });
4696
+ return response;
4697
+ }
4698
+ };
4699
+
4700
+ // src/generated/cfg_totp/totp__totp_management/client.ts
4701
+ var TotpManagement = class {
4702
+ static {
4703
+ __name(this, "TotpManagement");
4704
+ }
4705
+ constructor(client) {
4706
+ this.client = client;
4707
+ }
4708
+ /**
4709
+ * List all TOTP devices for user.
4710
+ */
4711
+ async totpDevicesList(...args) {
4712
+ const isParamsObject = args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0]);
4713
+ let params;
4714
+ if (isParamsObject) {
4715
+ params = args[0];
4716
+ } else {
4717
+ params = { page: args[0], page_size: args[1] };
4718
+ }
4719
+ const response = await this.client.request("GET", "/cfg/totp/devices/", { params });
4720
+ return response;
4721
+ }
4722
+ /**
4723
+ * Completely disable 2FA for account. Requires verification code.
4724
+ */
4725
+ async totpDisableCreate(data) {
4726
+ const response = await this.client.request("POST", "/cfg/totp/disable/", { body: data });
4727
+ return response;
4728
+ }
4729
+ };
4730
+
4731
+ // src/generated/cfg_totp/totp__totp_setup/client.ts
4732
+ var TotpSetup = class {
4733
+ static {
4734
+ __name(this, "TotpSetup");
4735
+ }
4736
+ constructor(client) {
4737
+ this.client = client;
4738
+ }
4739
+ /**
4740
+ * Start 2FA setup process. Creates a new TOTP device and returns QR code
4741
+ * for scanning.
4742
+ */
4743
+ async create(data) {
4744
+ const response = await this.client.request("POST", "/cfg/totp/setup/", { body: data });
4745
+ return response;
4746
+ }
4747
+ /**
4748
+ * Confirm 2FA setup with first valid code. Activates the device and
4749
+ * generates backup codes.
4750
+ */
4751
+ async confirmCreate(data) {
4752
+ const response = await this.client.request("POST", "/cfg/totp/setup/confirm/", { body: data });
4753
+ return response;
4754
+ }
4755
+ };
4756
+
4757
+ // src/generated/cfg_totp/totp__totp_verification/client.ts
4758
+ var TotpVerification = class {
4759
+ static {
4760
+ __name(this, "TotpVerification");
4761
+ }
4762
+ constructor(client) {
4763
+ this.client = client;
4764
+ }
4765
+ /**
4766
+ * Verify TOTP code for 2FA session. Completes authentication and returns
4767
+ * JWT tokens on success.
4768
+ */
4769
+ async totpVerifyCreate(data) {
4770
+ const response = await this.client.request("POST", "/cfg/totp/verify/", { body: data });
4771
+ return response;
4772
+ }
4773
+ /**
4774
+ * Verify backup recovery code for 2FA session. Alternative verification
4775
+ * method when TOTP device unavailable.
4776
+ */
4777
+ async totpVerifyBackupCreate(data) {
4778
+ const response = await this.client.request("POST", "/cfg/totp/verify/backup/", { body: data });
4779
+ return response;
4780
+ }
4781
+ };
4782
+
4783
+ // src/generated/cfg_totp/totp/client.ts
4784
+ var Totp = class {
4785
+ static {
4786
+ __name(this, "Totp");
4787
+ }
4788
+ constructor(client) {
4789
+ this.client = client;
4790
+ }
4791
+ /**
4792
+ * Delete a TOTP device. Requires verification code if removing the
4793
+ * last/primary device.
4794
+ */
4795
+ async devicesDestroy(id) {
4796
+ const response = await this.client.request("DELETE", `/cfg/totp/devices/${id}/`);
4797
+ return;
4798
+ }
4799
+ };
4800
+
4801
+ // src/generated/cfg_totp/http.ts
4802
+ var FetchAdapter4 = class {
4803
+ static {
4804
+ __name(this, "FetchAdapter");
4805
+ }
4806
+ async request(request) {
4807
+ const { method, url, headers, body, params, formData, binaryBody } = request;
4808
+ let finalUrl = url;
4809
+ if (params) {
4810
+ const searchParams = new URLSearchParams();
4811
+ Object.entries(params).forEach(([key, value]) => {
4812
+ if (value !== null && value !== void 0) {
4813
+ searchParams.append(key, String(value));
4814
+ }
4815
+ });
4816
+ const queryString = searchParams.toString();
4817
+ if (queryString) {
4818
+ finalUrl = url.includes("?") ? `${url}&${queryString}` : `${url}?${queryString}`;
4819
+ }
4820
+ }
4821
+ const finalHeaders = { ...headers };
4822
+ let requestBody;
4823
+ if (formData) {
4824
+ requestBody = formData;
4825
+ } else if (binaryBody) {
4826
+ finalHeaders["Content-Type"] = "application/octet-stream";
4827
+ requestBody = binaryBody;
4828
+ } else if (body) {
4829
+ finalHeaders["Content-Type"] = "application/json";
4830
+ requestBody = JSON.stringify(body);
4831
+ }
4832
+ const response = await fetch(finalUrl, {
4833
+ method,
4834
+ headers: finalHeaders,
4835
+ body: requestBody,
4836
+ credentials: "include"
4837
+ // Include Django session cookies
4838
+ });
4839
+ let data = null;
4840
+ const contentType = response.headers.get("content-type");
4841
+ if (response.status !== 204 && contentType?.includes("application/json")) {
4842
+ data = await response.json();
4843
+ } else if (response.status !== 204) {
4844
+ data = await response.text();
4845
+ }
4846
+ const responseHeaders = {};
4847
+ response.headers.forEach((value, key) => {
4848
+ responseHeaders[key] = value;
4849
+ });
4850
+ return {
4851
+ data,
4852
+ status: response.status,
4853
+ statusText: response.statusText,
4854
+ headers: responseHeaders
4855
+ };
4856
+ }
4857
+ };
4858
+
4859
+ // src/generated/cfg_totp/errors.ts
4860
+ var APIError4 = class extends Error {
4861
+ constructor(statusCode, statusText, response, url, message) {
4862
+ super(message || `HTTP ${statusCode}: ${statusText}`);
4863
+ this.statusCode = statusCode;
4864
+ this.statusText = statusText;
4865
+ this.response = response;
4866
+ this.url = url;
4867
+ this.name = "APIError";
4868
+ }
4869
+ static {
4870
+ __name(this, "APIError");
4871
+ }
4872
+ /**
4873
+ * Get error details from response.
4874
+ * DRF typically returns: { "detail": "Error message" } or { "field": ["error1", "error2"] }
4875
+ */
4876
+ get details() {
4877
+ if (typeof this.response === "object" && this.response !== null) {
4878
+ return this.response;
4879
+ }
4880
+ return null;
4881
+ }
4882
+ /**
4883
+ * Get field-specific validation errors from DRF.
4884
+ * Returns: { "field_name": ["error1", "error2"], ... }
4885
+ */
4886
+ get fieldErrors() {
4887
+ const details = this.details;
4888
+ if (!details) return null;
4889
+ const fieldErrors = {};
4890
+ for (const [key, value] of Object.entries(details)) {
4891
+ if (Array.isArray(value)) {
4892
+ fieldErrors[key] = value;
4893
+ }
4894
+ }
4895
+ return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
4896
+ }
4897
+ /**
4898
+ * Get single error message from DRF.
4899
+ * Checks for "detail", "message", or first field error.
4900
+ */
4901
+ get errorMessage() {
4902
+ const details = this.details;
4903
+ if (!details) return this.message;
4904
+ if (details.detail) {
4905
+ return Array.isArray(details.detail) ? details.detail.join(", ") : String(details.detail);
4906
+ }
4907
+ if (details.message) {
4908
+ return String(details.message);
4909
+ }
4910
+ const fieldErrors = this.fieldErrors;
4911
+ if (fieldErrors) {
4912
+ const firstField = Object.keys(fieldErrors)[0];
4913
+ if (firstField) {
4914
+ return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
4915
+ }
4916
+ }
4917
+ return this.message;
4918
+ }
4919
+ // Helper methods for common HTTP status codes
4920
+ get isValidationError() {
4921
+ return this.statusCode === 400;
4922
+ }
4923
+ get isAuthError() {
4924
+ return this.statusCode === 401;
4925
+ }
4926
+ get isPermissionError() {
4927
+ return this.statusCode === 403;
4928
+ }
4929
+ get isNotFoundError() {
4930
+ return this.statusCode === 404;
4931
+ }
4932
+ get isServerError() {
4933
+ return this.statusCode >= 500 && this.statusCode < 600;
4934
+ }
4935
+ };
4936
+ var NetworkError4 = class extends Error {
4937
+ constructor(message, url, originalError) {
4938
+ super(message);
4939
+ this.url = url;
4940
+ this.originalError = originalError;
4941
+ this.name = "NetworkError";
4942
+ }
4943
+ static {
4944
+ __name(this, "NetworkError");
4945
+ }
4946
+ };
4947
+
4948
+ // src/generated/cfg_totp/logger.ts
4949
+ import { createConsola as createConsola5 } from "consola";
4950
+ var DEFAULT_CONFIG4 = {
4951
+ enabled: process.env.NODE_ENV !== "production",
4952
+ logRequests: true,
4953
+ logResponses: true,
4954
+ logErrors: true,
4955
+ logBodies: true,
4956
+ logHeaders: false
4957
+ };
4958
+ var SENSITIVE_HEADERS4 = [
4959
+ "authorization",
4960
+ "cookie",
4961
+ "set-cookie",
4962
+ "x-api-key",
4963
+ "x-csrf-token"
4964
+ ];
4965
+ var APILogger4 = class {
4966
+ static {
4967
+ __name(this, "APILogger");
4968
+ }
4969
+ constructor(config = {}) {
4970
+ this.config = { ...DEFAULT_CONFIG4, ...config };
4971
+ this.consola = config.consola || createConsola5({
4972
+ level: this.config.enabled ? 4 : 0
4973
+ });
4974
+ }
4975
+ /**
4976
+ * Enable logging
4977
+ */
4978
+ enable() {
4979
+ this.config.enabled = true;
4980
+ }
4981
+ /**
4982
+ * Disable logging
4983
+ */
4984
+ disable() {
4985
+ this.config.enabled = false;
4986
+ }
4987
+ /**
4988
+ * Update configuration
4989
+ */
4990
+ setConfig(config) {
4991
+ this.config = { ...this.config, ...config };
4992
+ }
4993
+ /**
4994
+ * Filter sensitive headers
4995
+ */
4996
+ filterHeaders(headers) {
4997
+ if (!headers) return {};
4998
+ const filtered = {};
4999
+ Object.keys(headers).forEach((key) => {
5000
+ const lowerKey = key.toLowerCase();
5001
+ if (SENSITIVE_HEADERS4.includes(lowerKey)) {
5002
+ filtered[key] = "***";
5003
+ } else {
5004
+ filtered[key] = headers[key] || "";
5005
+ }
5006
+ });
5007
+ return filtered;
5008
+ }
5009
+ /**
5010
+ * Log request
5011
+ */
5012
+ logRequest(request) {
5013
+ if (!this.config.enabled || !this.config.logRequests) return;
5014
+ const { method, url, headers, body } = request;
5015
+ this.consola.start(`${method} ${url}`);
5016
+ if (this.config.logHeaders && headers) {
5017
+ this.consola.debug("Headers:", this.filterHeaders(headers));
5018
+ }
5019
+ if (this.config.logBodies && body) {
5020
+ this.consola.debug("Body:", body);
5021
+ }
5022
+ }
5023
+ /**
5024
+ * Log response
5025
+ */
5026
+ logResponse(request, response) {
5027
+ if (!this.config.enabled || !this.config.logResponses) return;
5028
+ const { method, url } = request;
5029
+ const { status, statusText, data, duration } = response;
5030
+ const statusColor = status >= 500 ? "red" : status >= 400 ? "yellow" : status >= 300 ? "cyan" : "green";
5031
+ this.consola.success(
5032
+ `${method} ${url} ${status} ${statusText} (${duration}ms)`
5033
+ );
5034
+ if (this.config.logBodies && data) {
5035
+ this.consola.debug("Response:", data);
5036
+ }
5037
+ }
5038
+ /**
5039
+ * Log error
5040
+ */
5041
+ logError(request, error) {
5042
+ if (!this.config.enabled || !this.config.logErrors) return;
5043
+ const { method, url } = request;
5044
+ const { message, statusCode, fieldErrors, duration } = error;
5045
+ this.consola.error(
5046
+ `${method} ${url} ${statusCode || "Network"} Error (${duration}ms)`
5047
+ );
5048
+ this.consola.error("Message:", message);
5049
+ if (fieldErrors && Object.keys(fieldErrors).length > 0) {
5050
+ this.consola.error("Field Errors:");
5051
+ Object.entries(fieldErrors).forEach(([field, errors]) => {
5052
+ errors.forEach((err) => {
5053
+ this.consola.error(` \u2022 ${field}: ${err}`);
5054
+ });
5055
+ });
5056
+ }
5057
+ }
5058
+ /**
5059
+ * Log general info
5060
+ */
5061
+ info(message, ...args) {
5062
+ if (!this.config.enabled) return;
5063
+ this.consola.info(message, ...args);
5064
+ }
5065
+ /**
5066
+ * Log warning
5067
+ */
5068
+ warn(message, ...args) {
5069
+ if (!this.config.enabled) return;
5070
+ this.consola.warn(message, ...args);
5071
+ }
5072
+ /**
5073
+ * Log error
5074
+ */
5075
+ error(message, ...args) {
5076
+ if (!this.config.enabled) return;
5077
+ this.consola.error(message, ...args);
5078
+ }
5079
+ /**
5080
+ * Log debug
5081
+ */
5082
+ debug(message, ...args) {
5083
+ if (!this.config.enabled) return;
5084
+ this.consola.debug(message, ...args);
5085
+ }
5086
+ /**
5087
+ * Log success
5088
+ */
5089
+ success(message, ...args) {
5090
+ if (!this.config.enabled) return;
5091
+ this.consola.success(message, ...args);
5092
+ }
5093
+ /**
5094
+ * Create a sub-logger with prefix
5095
+ */
5096
+ withTag(tag) {
5097
+ return this.consola.withTag(tag);
5098
+ }
5099
+ };
5100
+ var defaultLogger4 = new APILogger4();
5101
+
5102
+ // src/generated/cfg_totp/retry.ts
5103
+ import pRetry4, { AbortError as AbortError4 } from "p-retry";
5104
+ var DEFAULT_RETRY_CONFIG4 = {
5105
+ retries: 3,
5106
+ factor: 2,
5107
+ minTimeout: 1e3,
5108
+ maxTimeout: 6e4,
5109
+ randomize: true,
5110
+ onFailedAttempt: /* @__PURE__ */ __name(() => {
5111
+ }, "onFailedAttempt")
5112
+ };
5113
+ function shouldRetry4(error) {
5114
+ if (error instanceof NetworkError4) {
5115
+ return true;
5116
+ }
5117
+ if (error instanceof APIError4) {
5118
+ const status = error.statusCode;
5119
+ if (status >= 500 && status < 600) {
5120
+ return true;
5121
+ }
5122
+ if (status === 429) {
5123
+ return true;
5124
+ }
5125
+ return false;
5126
+ }
5127
+ return true;
5128
+ }
5129
+ __name(shouldRetry4, "shouldRetry");
5130
+ async function withRetry4(fn, config) {
5131
+ const finalConfig = { ...DEFAULT_RETRY_CONFIG4, ...config };
5132
+ return pRetry4(
5133
+ async () => {
5134
+ try {
5135
+ return await fn();
5136
+ } catch (error) {
5137
+ if (!shouldRetry4(error)) {
5138
+ throw new AbortError4(error);
5139
+ }
5140
+ throw error;
5141
+ }
5142
+ },
5143
+ {
5144
+ retries: finalConfig.retries,
5145
+ factor: finalConfig.factor,
5146
+ minTimeout: finalConfig.minTimeout,
5147
+ maxTimeout: finalConfig.maxTimeout,
5148
+ randomize: finalConfig.randomize,
5149
+ onFailedAttempt: finalConfig.onFailedAttempt ? (error) => {
5150
+ const pRetryError = error;
5151
+ finalConfig.onFailedAttempt({
5152
+ error: pRetryError,
5153
+ attemptNumber: pRetryError.attemptNumber,
5154
+ retriesLeft: pRetryError.retriesLeft
5155
+ });
5156
+ } : void 0
5157
+ }
5158
+ );
5159
+ }
5160
+ __name(withRetry4, "withRetry");
5161
+
5162
+ // src/generated/cfg_totp/client.ts
5163
+ var APIClient4 = class {
5164
+ constructor(baseUrl, options) {
5165
+ this.logger = null;
5166
+ this.retryConfig = null;
5167
+ this.baseUrl = baseUrl.replace(/\/$/, "");
5168
+ this.httpClient = options?.httpClient || new FetchAdapter4();
5169
+ if (options?.loggerConfig !== void 0) {
5170
+ this.logger = new APILogger4(options.loggerConfig);
5171
+ }
5172
+ if (options?.retryConfig !== void 0) {
5173
+ this.retryConfig = options.retryConfig;
5174
+ }
5175
+ this.backup_codes = new BackupCodes(this);
5176
+ this.totp_management = new TotpManagement(this);
5177
+ this.totp_setup = new TotpSetup(this);
5178
+ this.totp_verification = new TotpVerification(this);
5179
+ this.totp = new Totp(this);
5180
+ }
5181
+ static {
5182
+ __name(this, "APIClient");
5183
+ }
5184
+ /**
5185
+ * Get CSRF token from cookies (for SessionAuthentication).
5186
+ *
5187
+ * Returns null if cookie doesn't exist (JWT-only auth).
5188
+ */
5189
+ getCsrfToken() {
5190
+ const name = "csrftoken";
5191
+ const value = `; ${document.cookie}`;
5192
+ const parts = value.split(`; ${name}=`);
5193
+ if (parts.length === 2) {
5194
+ return parts.pop()?.split(";").shift() || null;
5195
+ }
5196
+ return null;
5197
+ }
5198
+ /**
5199
+ * Make HTTP request with Django CSRF and session handling.
5200
+ * Automatically retries on network errors and 5xx server errors.
5201
+ */
5202
+ async request(method, path, options) {
5203
+ if (this.retryConfig) {
5204
+ return withRetry4(() => this._makeRequest(method, path, options), {
5205
+ ...this.retryConfig,
5206
+ onFailedAttempt: /* @__PURE__ */ __name((info) => {
5207
+ if (this.logger) {
5208
+ this.logger.warn(
5209
+ `Retry attempt ${info.attemptNumber}/${info.retriesLeft + info.attemptNumber} for ${method} ${path}: ${info.error.message}`
5210
+ );
5211
+ }
5212
+ this.retryConfig?.onFailedAttempt?.(info);
5213
+ }, "onFailedAttempt")
5214
+ });
5215
+ }
5216
+ return this._makeRequest(method, path, options);
5217
+ }
5218
+ /**
5219
+ * Internal request method (without retry wrapper).
5220
+ * Used by request() method with optional retry logic.
5221
+ */
5222
+ async _makeRequest(method, path, options) {
5223
+ const url = this.baseUrl ? `${this.baseUrl}${path}` : path;
5224
+ const startTime = Date.now();
5225
+ const headers = {
5226
+ ...options?.headers || {}
5227
+ };
5228
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
5229
+ headers["Content-Type"] = "application/json";
5230
+ }
5231
+ if (this.logger) {
5232
+ this.logger.logRequest({
5233
+ method,
5234
+ url,
5235
+ headers,
5236
+ body: options?.formData || options?.body,
5237
+ timestamp: startTime
5238
+ });
5239
+ }
5240
+ try {
5241
+ const response = await this.httpClient.request({
5242
+ method,
5243
+ url,
5244
+ headers,
5245
+ params: options?.params,
5246
+ body: options?.body,
5247
+ formData: options?.formData,
5248
+ binaryBody: options?.binaryBody
5249
+ });
5250
+ const duration = Date.now() - startTime;
5251
+ if (response.status >= 400) {
5252
+ const error = new APIError4(
5253
+ response.status,
5254
+ response.statusText,
5255
+ response.data,
5256
+ url
5257
+ );
5258
+ if (this.logger) {
5259
+ this.logger.logError(
5260
+ {
5261
+ method,
5262
+ url,
5263
+ headers,
5264
+ body: options?.formData || options?.body,
5265
+ timestamp: startTime
5266
+ },
5267
+ {
5268
+ message: error.message,
5269
+ statusCode: response.status,
5270
+ duration,
5271
+ timestamp: Date.now()
5272
+ }
5273
+ );
5274
+ }
5275
+ throw error;
5276
+ }
5277
+ if (this.logger) {
5278
+ this.logger.logResponse(
5279
+ {
5280
+ method,
5281
+ url,
5282
+ headers,
5283
+ body: options?.formData || options?.body,
5284
+ timestamp: startTime
5285
+ },
5286
+ {
5287
+ status: response.status,
5288
+ statusText: response.statusText,
5289
+ data: response.data,
5290
+ duration,
5291
+ timestamp: Date.now()
5292
+ }
5293
+ );
5294
+ }
5295
+ return response.data;
5296
+ } catch (error) {
5297
+ const duration = Date.now() - startTime;
5298
+ if (error instanceof APIError4) {
5299
+ throw error;
5300
+ }
5301
+ const isCORSError = error instanceof TypeError && (error.message.toLowerCase().includes("cors") || error.message.toLowerCase().includes("failed to fetch") || error.message.toLowerCase().includes("network request failed"));
5302
+ if (this.logger) {
5303
+ if (isCORSError) {
5304
+ this.logger.error(`\u{1F6AB} CORS Error: ${method} ${url}`);
5305
+ this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
5306
+ this.logger.error(` \u2192 Configure security_domains parameter on the server`);
5307
+ } else {
5308
+ this.logger.error(`\u26A0\uFE0F Network Error: ${method} ${url}`);
5309
+ this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
5310
+ }
5311
+ }
5312
+ if (typeof window !== "undefined") {
5313
+ try {
5314
+ if (isCORSError) {
5315
+ window.dispatchEvent(new CustomEvent("cors-error", {
5316
+ detail: {
5317
+ url,
5318
+ method,
5319
+ error: error instanceof Error ? error.message : String(error),
5320
+ timestamp: /* @__PURE__ */ new Date()
5321
+ },
5322
+ bubbles: true,
5323
+ cancelable: false
5324
+ }));
5325
+ } else {
5326
+ window.dispatchEvent(new CustomEvent("network-error", {
5327
+ detail: {
5328
+ url,
5329
+ method,
5330
+ error: error instanceof Error ? error.message : String(error),
5331
+ timestamp: /* @__PURE__ */ new Date()
5332
+ },
5333
+ bubbles: true,
5334
+ cancelable: false
5335
+ }));
5336
+ }
5337
+ } catch (eventError) {
5338
+ }
5339
+ }
5340
+ const networkError = error instanceof Error ? new NetworkError4(error.message, url, error) : new NetworkError4("Unknown error", url);
5341
+ if (this.logger) {
5342
+ this.logger.logError(
5343
+ {
5344
+ method,
5345
+ url,
5346
+ headers,
5347
+ body: options?.formData || options?.body,
5348
+ timestamp: startTime
5349
+ },
5350
+ {
5351
+ message: networkError.message,
5352
+ duration,
5353
+ timestamp: Date.now()
5354
+ }
5355
+ );
5356
+ }
5357
+ throw networkError;
5358
+ }
5359
+ }
5360
+ };
5361
+
5362
+ // src/generated/cfg_totp/storage.ts
5363
+ var LocalStorageAdapter4 = class {
5364
+ static {
5365
+ __name(this, "LocalStorageAdapter");
5366
+ }
5367
+ constructor(logger2) {
5368
+ this.logger = logger2;
5369
+ }
5370
+ getItem(key) {
5371
+ try {
5372
+ if (typeof window !== "undefined" && window.localStorage) {
5373
+ const value = localStorage.getItem(key);
5374
+ this.logger?.debug(`LocalStorage.getItem("${key}"): ${value ? "found" : "not found"}`);
5375
+ return value;
5376
+ }
5377
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5378
+ } catch (error) {
5379
+ this.logger?.error("LocalStorage.getItem failed:", error);
5380
+ }
5381
+ return null;
5382
+ }
5383
+ setItem(key, value) {
5384
+ try {
5385
+ if (typeof window !== "undefined" && window.localStorage) {
5386
+ localStorage.setItem(key, value);
5387
+ this.logger?.debug(`LocalStorage.setItem("${key}"): success`);
5388
+ } else {
5389
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5390
+ }
5391
+ } catch (error) {
5392
+ this.logger?.error("LocalStorage.setItem failed:", error);
5393
+ }
5394
+ }
5395
+ removeItem(key) {
5396
+ try {
5397
+ if (typeof window !== "undefined" && window.localStorage) {
5398
+ localStorage.removeItem(key);
5399
+ this.logger?.debug(`LocalStorage.removeItem("${key}"): success`);
5400
+ } else {
5401
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5402
+ }
5403
+ } catch (error) {
5404
+ this.logger?.error("LocalStorage.removeItem failed:", error);
5405
+ }
5406
+ }
5407
+ };
5408
+
5409
+ // src/generated/cfg_totp/enums.ts
5410
+ var DeviceListStatus = /* @__PURE__ */ ((DeviceListStatus2) => {
5411
+ DeviceListStatus2["PENDING"] = "pending";
5412
+ DeviceListStatus2["ACTIVE"] = "active";
5413
+ DeviceListStatus2["DISABLED"] = "disabled";
5414
+ return DeviceListStatus2;
5415
+ })(DeviceListStatus || {});
5416
+
5417
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateRequest.schema.ts
5418
+ import { z as z60 } from "zod";
5419
+ var BackupCodesRegenerateRequestSchema = z60.object({
5420
+ code: z60.string().min(6).max(6)
5421
+ });
5422
+
5423
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateResponse.schema.ts
5424
+ import { z as z61 } from "zod";
5425
+ var BackupCodesRegenerateResponseSchema = z61.object({
5426
+ backup_codes: z61.array(z61.string()),
5427
+ warning: z61.string()
5428
+ });
5429
+
5430
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesStatus.schema.ts
5431
+ import { z as z62 } from "zod";
5432
+ var BackupCodesStatusSchema = z62.object({
5433
+ remaining_count: z62.int(),
5434
+ total_generated: z62.int(),
5435
+ warning: z62.string().nullable().optional()
5436
+ });
5437
+
5438
+ // src/generated/cfg_totp/_utils/schemas/ConfirmSetupRequest.schema.ts
5439
+ import { z as z63 } from "zod";
5440
+ var ConfirmSetupRequestSchema = z63.object({
5441
+ device_id: z63.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5442
+ code: z63.string().min(6).max(6)
5443
+ });
5444
+
5445
+ // src/generated/cfg_totp/_utils/schemas/ConfirmSetupResponse.schema.ts
5446
+ import { z as z64 } from "zod";
5447
+ var ConfirmSetupResponseSchema = z64.object({
5448
+ message: z64.string(),
5449
+ backup_codes: z64.array(z64.string()),
5450
+ backup_codes_warning: z64.string()
5451
+ });
5452
+
5453
+ // src/generated/cfg_totp/_utils/schemas/DeviceList.schema.ts
5454
+ import { z as z65 } from "zod";
5455
+ var DeviceListSchema = z65.object({
5456
+ id: z65.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5457
+ name: z65.string(),
5458
+ is_primary: z65.boolean(),
5459
+ status: z65.nativeEnum(DeviceListStatus),
5460
+ created_at: z65.iso.datetime(),
5461
+ confirmed_at: z65.iso.datetime().nullable(),
5462
+ last_used_at: z65.iso.datetime().nullable()
5463
+ });
5464
+
5465
+ // src/generated/cfg_totp/_utils/schemas/DisableRequest.schema.ts
5466
+ import { z as z66 } from "zod";
5467
+ var DisableRequestSchema = z66.object({
5468
+ code: z66.string().min(6).max(6)
5469
+ });
5470
+
5471
+ // src/generated/cfg_totp/_utils/schemas/PaginatedDeviceListList.schema.ts
5472
+ import { z as z67 } from "zod";
5473
+ var PaginatedDeviceListListSchema = z67.object({
5474
+ count: z67.int(),
5475
+ page: z67.int(),
5476
+ pages: z67.int(),
5477
+ page_size: z67.int(),
5478
+ has_next: z67.boolean(),
5479
+ has_previous: z67.boolean(),
5480
+ next_page: z67.int().nullable().optional(),
5481
+ previous_page: z67.int().nullable().optional(),
5482
+ results: z67.array(DeviceListSchema)
5483
+ });
5484
+
5485
+ // src/generated/cfg_totp/_utils/schemas/SetupRequest.schema.ts
5486
+ import { z as z68 } from "zod";
5487
+ var SetupRequestSchema = z68.object({
5488
+ device_name: z68.string().min(1).max(100).optional()
5489
+ });
5490
+
5491
+ // src/generated/cfg_totp/_utils/schemas/SetupResponse.schema.ts
5492
+ import { z as z69 } from "zod";
5493
+ var SetupResponseSchema = z69.object({
5494
+ device_id: z69.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5495
+ secret: z69.string(),
5496
+ provisioning_uri: z69.string(),
5497
+ qr_code_base64: z69.string(),
5498
+ expires_in: z69.int()
5499
+ });
5500
+
5501
+ // src/generated/cfg_totp/_utils/schemas/VerifyBackupRequest.schema.ts
5502
+ import { z as z70 } from "zod";
5503
+ var VerifyBackupRequestSchema = z70.object({
5504
+ session_id: z70.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5505
+ backup_code: z70.string().min(8).max(8)
5506
+ });
5507
+
5508
+ // src/generated/cfg_totp/_utils/schemas/VerifyRequest.schema.ts
5509
+ import { z as z71 } from "zod";
5510
+ var VerifyRequestSchema = z71.object({
5511
+ session_id: z71.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5512
+ code: z71.string().min(6).max(6)
5513
+ });
5514
+
5515
+ // src/generated/cfg_totp/_utils/schemas/VerifyResponse.schema.ts
5516
+ import { z as z72 } from "zod";
5517
+ var VerifyResponseSchema = z72.object({
5518
+ message: z72.string(),
5519
+ access_token: z72.string(),
5520
+ refresh_token: z72.string(),
5521
+ user: z72.record(z72.string(), z72.any()),
5522
+ remaining_backup_codes: z72.int().optional(),
5523
+ warning: z72.string().optional()
5524
+ });
5525
+
5526
+ // src/generated/cfg_totp/_utils/fetchers/totp__backup_codes.ts
5527
+ import { consola as consola10 } from "consola";
5528
+
5529
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_management.ts
5530
+ import { consola as consola11 } from "consola";
5531
+
5532
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_setup.ts
5533
+ import { consola as consola12 } from "consola";
5534
+
5535
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_verification.ts
5536
+ import { consola as consola13 } from "consola";
5537
+
5538
+ // src/generated/cfg_totp/index.ts
5539
+ var TOKEN_KEY4 = "auth_token";
5540
+ var REFRESH_TOKEN_KEY4 = "refresh_token";
5541
+ var API4 = class {
5542
+ constructor(baseUrl, options) {
5543
+ this._token = null;
5544
+ this._refreshToken = null;
5545
+ this.baseUrl = baseUrl;
5546
+ this.options = options;
5547
+ const logger2 = options?.loggerConfig ? new APILogger4(options.loggerConfig) : void 0;
5548
+ this.storage = options?.storage || new LocalStorageAdapter4(logger2);
5549
+ this._loadTokensFromStorage();
5550
+ this._client = new APIClient4(this.baseUrl, {
5551
+ retryConfig: this.options?.retryConfig,
5552
+ loggerConfig: this.options?.loggerConfig
5553
+ });
5554
+ this._injectAuthHeader();
5555
+ this.backup_codes = this._client.backup_codes;
5556
+ this.totp_management = this._client.totp_management;
5557
+ this.totp_setup = this._client.totp_setup;
5558
+ this.totp_verification = this._client.totp_verification;
5559
+ this.totp = this._client.totp;
5560
+ }
5561
+ static {
5562
+ __name(this, "API");
5563
+ }
5564
+ _loadTokensFromStorage() {
5565
+ this._token = this.storage.getItem(TOKEN_KEY4);
5566
+ this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY4);
5567
+ }
5568
+ _reinitClients() {
5569
+ this._client = new APIClient4(this.baseUrl, {
5570
+ retryConfig: this.options?.retryConfig,
5571
+ loggerConfig: this.options?.loggerConfig
5572
+ });
5573
+ this._injectAuthHeader();
5574
+ this.backup_codes = this._client.backup_codes;
5575
+ this.totp_management = this._client.totp_management;
5576
+ this.totp_setup = this._client.totp_setup;
5577
+ this.totp_verification = this._client.totp_verification;
5578
+ this.totp = this._client.totp;
5579
+ }
5580
+ _injectAuthHeader() {
5581
+ const originalRequest = this._client.request.bind(this._client);
5582
+ this._client.request = async (method, path, options) => {
5583
+ const token = this.getToken();
5584
+ const mergedOptions = {
5585
+ ...options,
5586
+ headers: {
5587
+ ...options?.headers || {},
5588
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
5589
+ }
5590
+ };
5591
+ return originalRequest(method, path, mergedOptions);
5592
+ };
5593
+ }
5594
+ /**
5595
+ * Get current JWT token
5596
+ */
5597
+ getToken() {
5598
+ return this.storage.getItem(TOKEN_KEY4);
5599
+ }
5600
+ /**
5601
+ * Get current refresh token
5602
+ */
5603
+ getRefreshToken() {
5604
+ return this.storage.getItem(REFRESH_TOKEN_KEY4);
5605
+ }
5606
+ /**
5607
+ * Set JWT token and refresh token
5608
+ * @param token - JWT access token
5609
+ * @param refreshToken - JWT refresh token (optional)
5610
+ */
5611
+ setToken(token, refreshToken) {
5612
+ this._token = token;
5613
+ this.storage.setItem(TOKEN_KEY4, token);
5614
+ if (refreshToken) {
5615
+ this._refreshToken = refreshToken;
5616
+ this.storage.setItem(REFRESH_TOKEN_KEY4, refreshToken);
5617
+ }
5618
+ this._reinitClients();
5619
+ }
5620
+ /**
5621
+ * Clear all tokens
5622
+ */
5623
+ clearTokens() {
5624
+ this._token = null;
5625
+ this._refreshToken = null;
5626
+ this.storage.removeItem(TOKEN_KEY4);
5627
+ this.storage.removeItem(REFRESH_TOKEN_KEY4);
5628
+ this._reinitClients();
5629
+ }
5630
+ /**
5631
+ * Check if user is authenticated
5632
+ */
5633
+ isAuthenticated() {
5634
+ return !!this.getToken();
5635
+ }
5636
+ /**
5637
+ * Update base URL and reinitialize clients
5638
+ * @param url - New base URL
5639
+ */
5640
+ setBaseUrl(url) {
5641
+ this.baseUrl = url;
5642
+ this._reinitClients();
5643
+ }
5644
+ /**
5645
+ * Get current base URL
5646
+ */
5647
+ getBaseUrl() {
5648
+ return this.baseUrl;
5649
+ }
5650
+ /**
5651
+ * Get OpenAPI schema path
5652
+ * @returns Path to the OpenAPI schema JSON file
5653
+ *
5654
+ * Note: The OpenAPI schema is available in the schema.json file.
5655
+ * You can load it dynamically using:
5656
+ * ```typescript
5657
+ * const schema = await fetch('./schema.json').then(r => r.json());
5658
+ * // or using fs in Node.js:
5659
+ * // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
5660
+ * ```
5661
+ */
5662
+ getSchemaPath() {
5663
+ return "./schema.json";
5664
+ }
5665
+ };
5666
+
5667
+ // src/generated/cfg_webpush/_utils/hooks/webpush__web_push.ts
5668
+ import useSWR3 from "swr";
5669
+ import { useSWRConfig as useSWRConfig5 } from "swr";
5670
+
5671
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_admin_api.ts
5672
+ import { useSWRConfig as useSWRConfig6 } from "swr";
5673
+
5674
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_auth.ts
5675
+ import useSWR4 from "swr";
5676
+
5677
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_monitoring.ts
5678
+ import useSWR5 from "swr";
5679
+
5680
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_testing.ts
5681
+ import { useSWRConfig as useSWRConfig7 } from "swr";
5682
+
5683
+ // src/generated/cfg_totp/_utils/hooks/totp__backup_codes.ts
5684
+ import useSWR6 from "swr";
5685
+ import { useSWRConfig as useSWRConfig8 } from "swr";
5686
+
5687
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_management.ts
5688
+ import useSWR7 from "swr";
5689
+ import { useSWRConfig as useSWRConfig9 } from "swr";
5690
+
5691
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_setup.ts
5692
+ import { useSWRConfig as useSWRConfig10 } from "swr";
5693
+
5694
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_verification.ts
5695
+ import { useSWRConfig as useSWRConfig11 } from "swr";
5696
+
5697
+ // src/generated/cfg_totp/_utils/hooks/totp.ts
5698
+ import { useSWRConfig as useSWRConfig12 } from "swr";
5699
+
5700
+ // src/clients.ts
5701
+ var isStaticBuild3 = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
5702
+ var apiUrl2 = isStaticBuild3 ? "" : process.env.NEXT_PUBLIC_API_URL || "";
5703
+ var storage = new LocalStorageAdapter();
5704
+ var apiAccounts = new API(apiUrl2, { storage });
5705
+ var apiTotp = new API4(apiUrl2, { storage });
5706
+ var apiWebPush = new API3(apiUrl2, { storage });
5707
+ var apiCentrifugo = new API2(apiUrl2, { storage });
5708
+
5709
+ // src/auth/hooks/useTwoFactor.ts
5710
+ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
5711
+ const { onSuccess, onError, redirectUrl, skipRedirect = false } = options;
5712
+ const router = useCfgRouter3();
5713
+ const [isLoading, setIsLoading] = useState5(false);
5714
+ const [error, setError] = useState5(null);
5715
+ const [warning, setWarning] = useState5(null);
5716
+ const [remainingBackupCodes, setRemainingBackupCodes] = useState5(null);
5717
+ const clearError = useCallback5(() => {
5718
+ setError(null);
5719
+ }, []);
5720
+ const handleSuccess = useCallback5((response) => {
5721
+ apiAccounts.setToken(response.access_token, response.refresh_token);
5722
+ if (response.warning) {
5723
+ setWarning(response.warning);
5724
+ }
5725
+ if (response.remaining_backup_codes !== void 0) {
5726
+ setRemainingBackupCodes(response.remaining_backup_codes);
5727
+ }
5728
+ Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
5729
+ category: "auth" /* AUTH */,
5730
+ label: "2fa"
5731
+ });
5732
+ if (response.user?.id) {
5733
+ Analytics.setUser(String(response.user.id));
5734
+ }
5735
+ onSuccess?.(response.user);
5736
+ if (!skipRedirect) {
5737
+ const finalRedirectUrl = redirectUrl || "/dashboard";
5738
+ authLogger.info("2FA successful, redirecting to:", finalRedirectUrl);
5739
+ router.hardPush(finalRedirectUrl);
5740
+ }
5741
+ }, [onSuccess, redirectUrl, router, skipRedirect]);
5742
+ const verifyTOTP = useCallback5(async (sessionId, code) => {
5743
+ if (!sessionId) {
5744
+ const msg = "Missing 2FA session ID";
5745
+ setError(msg);
5746
+ onError?.(msg);
5747
+ return false;
5748
+ }
5749
+ if (!code || code.length !== 6) {
5750
+ const msg = "Please enter a 6-digit code";
5751
+ setError(msg);
5752
+ onError?.(msg);
5753
+ return false;
5754
+ }
5755
+ setIsLoading(true);
5756
+ setError(null);
5757
+ try {
5758
+ authLogger.info("Verifying TOTP code...");
5759
+ const response = await apiTotp.totp_verification.totpVerifyCreate({
5760
+ session_id: sessionId,
5761
+ code
5762
+ });
5763
+ if (!response.access_token || !response.refresh_token) {
5764
+ throw new Error("Invalid response from 2FA verification");
5765
+ }
5766
+ handleSuccess(response);
5767
+ return true;
5768
+ } catch (err) {
5769
+ const errorMessage = err instanceof Error ? err.message : "Invalid verification code";
5770
+ authLogger.error("2FA TOTP verification error:", err);
5771
+ setError(errorMessage);
5772
+ onError?.(errorMessage);
5773
+ Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
5774
+ category: "auth" /* AUTH */,
5775
+ label: "2fa-totp"
5776
+ });
5777
+ return false;
5778
+ } finally {
5779
+ setIsLoading(false);
5780
+ }
5781
+ }, [handleSuccess, onError]);
5782
+ const verifyBackupCode = useCallback5(async (sessionId, backupCode) => {
5783
+ if (!sessionId) {
5784
+ const msg = "Missing 2FA session ID";
5785
+ setError(msg);
5786
+ onError?.(msg);
5787
+ return false;
5788
+ }
5789
+ if (!backupCode || backupCode.length < 8) {
5790
+ const msg = "Please enter your backup code";
5791
+ setError(msg);
5792
+ onError?.(msg);
5793
+ return false;
5794
+ }
5795
+ setIsLoading(true);
5796
+ setError(null);
5797
+ try {
5798
+ authLogger.info("Verifying backup code...");
5799
+ const response = await apiTotp.totp_verification.totpVerifyBackupCreate({
5800
+ session_id: sessionId,
5801
+ backup_code: backupCode.replace(/\s+/g, "")
5802
+ // Remove spaces
5803
+ });
5804
+ if (!response.access_token || !response.refresh_token) {
5805
+ throw new Error("Invalid response from backup code verification");
5806
+ }
5807
+ handleSuccess(response);
5808
+ return true;
5809
+ } catch (err) {
5810
+ const errorMessage = err instanceof Error ? err.message : "Invalid backup code";
5811
+ authLogger.error("2FA backup code verification error:", err);
5812
+ setError(errorMessage);
5813
+ onError?.(errorMessage);
5814
+ Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
5815
+ category: "auth" /* AUTH */,
5816
+ label: "2fa-backup"
5817
+ });
5818
+ return false;
5819
+ } finally {
5820
+ setIsLoading(false);
5821
+ }
5822
+ }, [handleSuccess, onError]);
5823
+ return {
5824
+ isLoading,
5825
+ error,
5826
+ warning,
5827
+ remainingBackupCodes,
5828
+ verifyTOTP,
5829
+ verifyBackupCode,
5830
+ clearError
5831
+ };
5832
+ }, "useTwoFactor");
5833
+
5834
+ // src/auth/hooks/useAuthForm.ts
5835
+ var useAuthForm = /* @__PURE__ */ __name((options) => {
5836
+ const {
5837
+ onIdentifierSuccess,
5838
+ onOTPSuccess,
5839
+ onError,
5840
+ sourceUrl,
5841
+ redirectUrl,
5842
+ requireTermsAcceptance = false,
5843
+ authPath = "/auth"
5844
+ } = options;
5845
+ const formState = useAuthFormState();
5846
+ const validation = useAuthValidation();
5847
+ const isAutoSubmitFromUrlRef = useRef3(false);
5848
+ const {
5849
+ requestOTP,
5850
+ verifyOTP,
5851
+ getSavedEmail,
5852
+ saveEmail,
5853
+ clearSavedEmail,
5854
+ getSavedPhone,
5855
+ savePhone,
5856
+ clearSavedPhone
5857
+ } = useAuth();
5858
+ const {
5859
+ identifier,
5860
+ channel,
5861
+ otp,
5862
+ isLoading,
5863
+ acceptedTerms,
5864
+ twoFactorSessionId,
5865
+ twoFactorCode,
5866
+ useBackupCode,
5867
+ setIdentifier,
5868
+ setChannel,
5869
+ setOtp,
5870
+ setStep,
5871
+ setError,
5872
+ setIsLoading,
5873
+ clearError,
5874
+ setTwoFactorSessionId,
5875
+ setShouldPrompt2FA,
5876
+ setTwoFactorCode,
5877
+ setUseBackupCode
5878
+ } = formState;
5879
+ const twoFactor = useTwoFactor({
5880
+ onSuccess: /* @__PURE__ */ __name(() => {
5881
+ authLogger.info("2FA verification successful, showing success screen");
5882
+ setStep("success");
5883
+ onOTPSuccess?.();
5884
+ }, "onSuccess"),
5885
+ onError: /* @__PURE__ */ __name((error) => {
5886
+ setError(error);
5887
+ onError?.(error);
5888
+ }, "onError"),
5889
+ redirectUrl,
5890
+ skipRedirect: true
5891
+ // We handle navigation via success step
5892
+ });
5893
+ const { detectChannelFromIdentifier: detectChannelFromIdentifier2, validateIdentifier: validateIdentifier2 } = validation;
5894
+ const saveIdentifierToStorage = useCallback6((id, ch) => {
5895
+ if (ch === "email") {
5896
+ saveEmail(id);
5897
+ clearSavedPhone();
5898
+ } else {
5899
+ savePhone(id);
5900
+ clearSavedEmail();
5901
+ }
5902
+ }, [saveEmail, savePhone, clearSavedEmail, clearSavedPhone]);
5903
+ useEffect4(() => {
5904
+ const savedPhone = getSavedPhone();
5905
+ const savedEmail = getSavedEmail();
5906
+ if (savedPhone) {
5907
+ setIdentifier(savedPhone);
5908
+ setChannel("phone");
5909
+ } else if (savedEmail) {
5910
+ setIdentifier(savedEmail);
5911
+ setChannel("email");
5912
+ }
5913
+ }, [getSavedEmail, getSavedPhone, setIdentifier, setChannel]);
5914
+ useEffect4(() => {
5915
+ if (identifier) {
5916
+ const detected = detectChannelFromIdentifier2(identifier);
5917
+ if (detected && detected !== channel) {
5918
+ setChannel(detected);
5919
+ }
5920
+ }
5921
+ }, [identifier, channel, detectChannelFromIdentifier2, setChannel]);
5922
+ const handleIdentifierSubmit = useCallback6(async (e) => {
5923
+ e.preventDefault();
5924
+ if (!identifier) {
5925
+ const msg = channel === "phone" ? "Please enter your phone number" : "Please enter your email address";
5926
+ setError(msg);
5927
+ onError?.(msg);
5928
+ return;
5929
+ }
5930
+ if (!validateIdentifier2(identifier, channel)) {
5931
+ const msg = channel === "phone" ? "Please enter a valid phone number (e.g., +1234567890)" : "Please enter a valid email address";
5932
+ setError(msg);
5933
+ onError?.(msg);
5934
+ return;
4795
5935
  }
4796
5936
  if (requireTermsAcceptance && !acceptedTerms) {
4797
- const message = "Please accept the Terms of Service and Privacy Policy";
4798
- setError(message);
4799
- onError?.(message);
5937
+ const msg = "Please accept the Terms of Service and Privacy Policy";
5938
+ setError(msg);
5939
+ onError?.(msg);
4800
5940
  return;
4801
5941
  }
4802
5942
  setIsLoading(true);
@@ -4804,185 +5944,206 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4804
5944
  try {
4805
5945
  const result = await requestOTP(identifier, channel, sourceUrl);
4806
5946
  if (result.success) {
4807
- if (channel === "email") {
4808
- saveEmail(identifier);
4809
- setSavedPhone("");
4810
- } else if (channel === "phone") {
4811
- savePhone(identifier);
4812
- setSavedEmail("");
4813
- }
4814
- setSavedTermsAccepted(true);
5947
+ saveIdentifierToStorage(identifier, channel);
4815
5948
  setStep("otp");
4816
5949
  onIdentifierSuccess?.(identifier, channel);
4817
5950
  } else {
4818
5951
  setError(result.message);
4819
5952
  onError?.(result.message);
4820
5953
  }
4821
- } catch (error2) {
4822
- const message = "An unexpected error occurred";
4823
- setError(message);
4824
- onError?.(message);
5954
+ } catch {
5955
+ const msg = "An unexpected error occurred";
5956
+ setError(msg);
5957
+ onError?.(msg);
4825
5958
  } finally {
4826
5959
  setIsLoading(false);
4827
5960
  }
4828
- }, [identifier, channel, acceptedTerms, validateIdentifier, requestOTP, saveEmail, clearError, setSavedTermsAccepted, onIdentifierSuccess, onError, sourceUrl]);
4829
- const handleOTPSubmit = useCallback3(async (e) => {
4830
- e.preventDefault();
4831
- if (!otp || otp.length < 6) {
4832
- const message = "Please enter the 6-digit verification code";
4833
- setError(message);
4834
- onError?.(message);
4835
- return;
5961
+ }, [
5962
+ identifier,
5963
+ channel,
5964
+ acceptedTerms,
5965
+ requireTermsAcceptance,
5966
+ validateIdentifier2,
5967
+ requestOTP,
5968
+ saveIdentifierToStorage,
5969
+ setError,
5970
+ setIsLoading,
5971
+ setStep,
5972
+ clearError,
5973
+ onIdentifierSuccess,
5974
+ onError,
5975
+ sourceUrl
5976
+ ]);
5977
+ const submitOTP = useCallback6(async (submitIdentifier, submitOtp, submitChannel) => {
5978
+ if (!submitOtp || submitOtp.length < 6) {
5979
+ const msg = "Please enter the 6-digit verification code";
5980
+ setError(msg);
5981
+ onError?.(msg);
5982
+ return false;
4836
5983
  }
4837
5984
  setIsLoading(true);
4838
5985
  clearError();
4839
5986
  try {
4840
- const result = await verifyOTP(identifier, otp, channel, sourceUrl, redirectUrl);
5987
+ const result = await verifyOTP(submitIdentifier, submitOtp, submitChannel, sourceUrl, redirectUrl, true);
5988
+ if (result.requires_2fa && result.session_id) {
5989
+ authLogger.info("2FA required after OTP verification");
5990
+ setTwoFactorSessionId(result.session_id);
5991
+ setShouldPrompt2FA(result.should_prompt_2fa || false);
5992
+ setStep("2fa");
5993
+ saveIdentifierToStorage(submitIdentifier, submitChannel);
5994
+ return true;
5995
+ }
4841
5996
  if (result.success) {
4842
- if (channel === "email") {
4843
- setSavedEmail(identifier);
4844
- setSavedPhone("");
4845
- } else if (channel === "phone") {
4846
- setSavedPhone(identifier);
4847
- setSavedEmail("");
5997
+ saveIdentifierToStorage(submitIdentifier, submitChannel);
5998
+ if (result.should_prompt_2fa) {
5999
+ authLogger.info("OTP verification successful, prompting 2FA setup");
6000
+ setShouldPrompt2FA(true);
6001
+ setStep("2fa-setup");
6002
+ onOTPSuccess?.();
6003
+ return true;
4848
6004
  }
6005
+ authLogger.info("OTP verification successful, showing success screen");
6006
+ setStep("success");
4849
6007
  onOTPSuccess?.();
6008
+ return true;
4850
6009
  } else {
4851
6010
  setError(result.message);
4852
6011
  onError?.(result.message);
6012
+ return false;
4853
6013
  }
4854
- } catch (error2) {
4855
- const message = "An unexpected error occurred";
4856
- setError(message);
4857
- onError?.(message);
6014
+ } catch {
6015
+ const msg = "An unexpected error occurred";
6016
+ setError(msg);
6017
+ onError?.(msg);
6018
+ return false;
4858
6019
  } finally {
4859
6020
  setIsLoading(false);
4860
6021
  }
4861
- }, [identifier, otp, channel, verifyOTP, clearError, setSavedEmail, onOTPSuccess, onError, sourceUrl, redirectUrl]);
4862
- const handleResendOTP = useCallback3(async () => {
6022
+ }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
6023
+ const handleOTPSubmit = useCallback6(async (e) => {
6024
+ e.preventDefault();
6025
+ await submitOTP(identifier, otp, channel);
6026
+ }, [identifier, otp, channel, submitOTP]);
6027
+ const handleResendOTP = useCallback6(async () => {
4863
6028
  setIsLoading(true);
4864
6029
  clearError();
4865
6030
  try {
4866
6031
  const result = await requestOTP(identifier, channel, sourceUrl);
4867
6032
  if (result.success) {
4868
- if (channel === "email") {
4869
- saveEmail(identifier);
4870
- setSavedPhone("");
4871
- } else if (channel === "phone") {
4872
- savePhone(identifier);
4873
- setSavedEmail("");
4874
- }
6033
+ saveIdentifierToStorage(identifier, channel);
4875
6034
  setOtp("");
4876
6035
  } else {
4877
6036
  setError(result.message);
4878
6037
  onError?.(result.message);
4879
6038
  }
4880
- } catch (error2) {
4881
- const message = "Failed to resend verification code";
4882
- setError(message);
4883
- onError?.(message);
6039
+ } catch {
6040
+ const msg = "Failed to resend verification code";
6041
+ setError(msg);
6042
+ onError?.(msg);
4884
6043
  } finally {
4885
6044
  setIsLoading(false);
4886
6045
  }
4887
- }, [identifier, channel, requestOTP, saveEmail, clearError, setOtp, onError, sourceUrl]);
4888
- const handleBackToIdentifier = useCallback3(() => {
6046
+ }, [identifier, channel, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, onError, sourceUrl]);
6047
+ const handleBackToIdentifier = useCallback6(() => {
4889
6048
  setStep("identifier");
4890
6049
  clearError();
4891
- }, [clearError]);
4892
- const forceOTPStep = useCallback3(() => {
6050
+ }, [setStep, clearError]);
6051
+ const forceOTPStep = useCallback6(() => {
4893
6052
  setStep("otp");
4894
6053
  clearError();
4895
- }, [clearError]);
4896
- const handleAcceptedTermsChange = useCallback3((checked) => {
4897
- setAcceptedTerms(checked);
4898
- setSavedTermsAccepted(checked);
4899
- }, [setSavedTermsAccepted]);
6054
+ }, [setStep, clearError]);
6055
+ const handle2FASubmit = useCallback6(async (e) => {
6056
+ e.preventDefault();
6057
+ if (!twoFactorSessionId) {
6058
+ const msg = "Missing 2FA session";
6059
+ setError(msg);
6060
+ onError?.(msg);
6061
+ return;
6062
+ }
6063
+ if (useBackupCode) {
6064
+ await twoFactor.verifyBackupCode(twoFactorSessionId, twoFactorCode);
6065
+ } else {
6066
+ await twoFactor.verifyTOTP(twoFactorSessionId, twoFactorCode);
6067
+ }
6068
+ }, [twoFactorSessionId, twoFactorCode, useBackupCode, twoFactor, setError, onError]);
6069
+ const handleUseBackupCode = useCallback6(() => {
6070
+ setUseBackupCode(true);
6071
+ setTwoFactorCode("");
6072
+ clearError();
6073
+ }, [setUseBackupCode, setTwoFactorCode, clearError]);
6074
+ const handleUseTOTP = useCallback6(() => {
6075
+ setUseBackupCode(false);
6076
+ setTwoFactorCode("");
6077
+ clearError();
6078
+ }, [setUseBackupCode, setTwoFactorCode, clearError]);
4900
6079
  useAutoAuth({
4901
6080
  allowedPaths: [authPath],
4902
- onOTPDetected: /* @__PURE__ */ __name((otp2) => {
4903
- authLogger.info("OTP detected, auto-submitting");
4904
- const savedEmail2 = getSavedEmail();
4905
- const savedPhone2 = getSavedPhone();
4906
- if (savedPhone2) {
4907
- setIdentifier(savedPhone2);
4908
- setChannel("phone");
4909
- } else if (savedEmail2) {
4910
- setIdentifier(savedEmail2);
4911
- setChannel("email");
6081
+ onOTPDetected: /* @__PURE__ */ __name((detectedOtp) => {
6082
+ if (isAutoSubmitFromUrlRef.current || isLoading) return;
6083
+ isAutoSubmitFromUrlRef.current = true;
6084
+ authLogger.info("OTP detected from URL, auto-submitting");
6085
+ const savedPhone = getSavedPhone();
6086
+ const savedEmail = getSavedEmail();
6087
+ let autoIdentifier = "";
6088
+ let autoChannel = "email";
6089
+ if (savedPhone) {
6090
+ autoIdentifier = savedPhone;
6091
+ autoChannel = "phone";
6092
+ } else if (savedEmail) {
6093
+ autoIdentifier = savedEmail;
6094
+ autoChannel = "email";
6095
+ }
6096
+ if (!autoIdentifier) {
6097
+ authLogger.warn("No saved identifier found for auto-submit");
6098
+ isAutoSubmitFromUrlRef.current = false;
6099
+ return;
4912
6100
  }
4913
- setOtp(otp2);
6101
+ setIdentifier(autoIdentifier);
6102
+ setChannel(autoChannel);
6103
+ setOtp(detectedOtp);
4914
6104
  setStep("otp");
4915
- setTimeout(() => {
4916
- const fakeEvent = { preventDefault: /* @__PURE__ */ __name(() => {
4917
- }, "preventDefault") };
4918
- handleOTPSubmit(fakeEvent);
4919
- }, 200);
4920
- }, "onOTPDetected"),
4921
- cleanupUrl: true
4922
- });
4923
- return {
4924
- // Form state
4925
- identifier,
4926
- channel,
4927
- otp,
4928
- isLoading,
4929
- acceptedTerms,
4930
- step,
4931
- error,
4932
- // Form handlers
4933
- setIdentifier,
4934
- setChannel,
4935
- setOtp,
4936
- setAcceptedTerms: handleAcceptedTermsChange,
4937
- setError,
4938
- clearError,
4939
- // Auth handlers
6105
+ setTimeout(async () => {
6106
+ try {
6107
+ await submitOTP(autoIdentifier, detectedOtp, autoChannel);
6108
+ } finally {
6109
+ isAutoSubmitFromUrlRef.current = false;
6110
+ }
6111
+ }, 100);
6112
+ }, "onOTPDetected"),
6113
+ cleanupUrl: true
6114
+ });
6115
+ return {
6116
+ // State
6117
+ ...formState,
6118
+ // Submit handlers
4940
6119
  handleIdentifierSubmit,
4941
6120
  handleOTPSubmit,
4942
6121
  handleResendOTP,
4943
6122
  handleBackToIdentifier,
4944
6123
  forceOTPStep,
4945
- // Utility methods
4946
- detectChannelFromIdentifier,
4947
- validateIdentifier
6124
+ // 2FA handlers
6125
+ handle2FASubmit,
6126
+ handleUseBackupCode,
6127
+ handleUseTOTP,
6128
+ // Validation
6129
+ ...validation,
6130
+ // Auto-submit ref
6131
+ isAutoSubmittingFromUrl: isAutoSubmitFromUrlRef,
6132
+ // 2FA state from hook (for loading indicator)
6133
+ is2FALoading: twoFactor.isLoading,
6134
+ twoFactorWarning: twoFactor.warning
4948
6135
  };
4949
6136
  }, "useAuthForm");
4950
6137
 
4951
6138
  // src/auth/hooks/useGithubAuth.ts
4952
- import { useCallback as useCallback4, useState as useState7 } from "react";
6139
+ import { useCallback as useCallback7, useState as useState6 } from "react";
4953
6140
  import { useCfgRouter as useCfgRouter4 } from "@djangocfg/ui-nextjs/hooks";
4954
-
4955
- // src/generated/cfg_webpush/_utils/hooks/webpush__web_push.ts
4956
- import useSWR3 from "swr";
4957
- import { useSWRConfig as useSWRConfig5 } from "swr";
4958
-
4959
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_admin_api.ts
4960
- import { useSWRConfig as useSWRConfig6 } from "swr";
4961
-
4962
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_auth.ts
4963
- import useSWR4 from "swr";
4964
-
4965
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_monitoring.ts
4966
- import useSWR5 from "swr";
4967
-
4968
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_testing.ts
4969
- import { useSWRConfig as useSWRConfig7 } from "swr";
4970
-
4971
- // src/clients.ts
4972
- var isStaticBuild3 = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
4973
- var apiUrl2 = isStaticBuild3 ? "" : process.env.NEXT_PUBLIC_API_URL || "";
4974
- var storage = new LocalStorageAdapter();
4975
- var apiAccounts = new API(apiUrl2, { storage });
4976
- var apiWebPush = new API3(apiUrl2, { storage });
4977
- var apiCentrifugo = new API2(apiUrl2, { storage });
4978
-
4979
- // src/auth/hooks/useGithubAuth.ts
4980
6141
  var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
4981
- const { sourceUrl, onSuccess, onError, redirectUrl } = options;
6142
+ const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
4982
6143
  const router = useCfgRouter4();
4983
- const [isLoading, setIsLoading] = useState7(false);
4984
- const [error, setError] = useState7(null);
4985
- const startGithubAuth = useCallback4(async () => {
6144
+ const [isLoading, setIsLoading] = useState6(false);
6145
+ const [error, setError] = useState6(null);
6146
+ const startGithubAuth = useCallback7(async () => {
4986
6147
  setIsLoading(true);
4987
6148
  setError(null);
4988
6149
  try {
@@ -5016,7 +6177,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5016
6177
  setIsLoading(false);
5017
6178
  }
5018
6179
  }, [sourceUrl, onError]);
5019
- const handleGithubCallback = useCallback4(async (code, state) => {
6180
+ const handleGithubCallback = useCallback7(async (code, state) => {
5020
6181
  setIsLoading(true);
5021
6182
  setError(null);
5022
6183
  try {
@@ -5033,6 +6194,15 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5033
6194
  code,
5034
6195
  state
5035
6196
  });
6197
+ if (response.requires_2fa && response.session_id) {
6198
+ authLogger.info("GitHub OAuth requires 2FA, session:", response.session_id);
6199
+ Analytics.event("auth_oauth_start" /* AUTH_OAUTH_START */, {
6200
+ category: "auth" /* AUTH */,
6201
+ label: "github-2fa-required"
6202
+ });
6203
+ onRequires2FA?.(response.session_id, response.should_prompt_2fa || false);
6204
+ return;
6205
+ }
5036
6206
  if (!response.access || !response.refresh) {
5037
6207
  throw new Error("Invalid response from OAuth callback");
5038
6208
  }
@@ -5046,8 +6216,10 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5046
6216
  Analytics.setUser(String(response.user.id));
5047
6217
  }
5048
6218
  onSuccess?.(response.user, response.is_new_user || false);
5049
- const finalRedirectUrl = redirectUrl || "/dashboard";
5050
- router.hardPush(finalRedirectUrl);
6219
+ if (!skipRedirect) {
6220
+ const finalRedirectUrl = redirectUrl || "/dashboard";
6221
+ router.hardPush(finalRedirectUrl);
6222
+ }
5051
6223
  } catch (err) {
5052
6224
  const errorMessage = err instanceof Error ? err.message : "GitHub authentication failed";
5053
6225
  authLogger.error("GitHub OAuth callback error:", err);
@@ -5069,6 +6241,291 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5069
6241
  };
5070
6242
  }, "useGithubAuth");
5071
6243
 
6244
+ // src/auth/hooks/useTwoFactorSetup.ts
6245
+ import { useCallback as useCallback8, useState as useState7 } from "react";
6246
+ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
6247
+ const { onComplete, onError } = options;
6248
+ const [isLoading, setIsLoading] = useState7(false);
6249
+ const [error, setError] = useState7(null);
6250
+ const [setupData, setSetupData] = useState7(null);
6251
+ const [backupCodes, setBackupCodes] = useState7(null);
6252
+ const [backupCodesWarning, setBackupCodesWarning] = useState7(null);
6253
+ const [setupStep, setSetupStep] = useState7("idle");
6254
+ const clearError = useCallback8(() => {
6255
+ setError(null);
6256
+ }, []);
6257
+ const resetSetup = useCallback8(() => {
6258
+ setSetupData(null);
6259
+ setBackupCodes(null);
6260
+ setBackupCodesWarning(null);
6261
+ setSetupStep("idle");
6262
+ setError(null);
6263
+ }, []);
6264
+ const startSetup = useCallback8(async (deviceName) => {
6265
+ setIsLoading(true);
6266
+ setError(null);
6267
+ setSetupStep("scanning");
6268
+ try {
6269
+ authLogger.info("Starting 2FA setup...");
6270
+ const response = await apiTotp.totp_setup.create({
6271
+ device_name: deviceName
6272
+ });
6273
+ const data = {
6274
+ deviceId: response.device_id,
6275
+ secret: response.secret,
6276
+ provisioningUri: response.provisioning_uri,
6277
+ qrCodeBase64: response.qr_code_base64,
6278
+ expiresIn: response.expires_in
6279
+ };
6280
+ setSetupData(data);
6281
+ authLogger.info("2FA setup initiated, expires in:", data.expiresIn, "seconds");
6282
+ return data;
6283
+ } catch (err) {
6284
+ const errorMessage = err instanceof Error ? err.message : "Failed to start 2FA setup";
6285
+ authLogger.error("2FA setup error:", err);
6286
+ setError(errorMessage);
6287
+ setSetupStep("idle");
6288
+ onError?.(errorMessage);
6289
+ return null;
6290
+ } finally {
6291
+ setIsLoading(false);
6292
+ }
6293
+ }, [onError]);
6294
+ const confirmSetup = useCallback8(async (code) => {
6295
+ if (!setupData) {
6296
+ const msg = "Setup not started. Call startSetup() first.";
6297
+ setError(msg);
6298
+ onError?.(msg);
6299
+ return null;
6300
+ }
6301
+ if (!code || code.length !== 6) {
6302
+ const msg = "Please enter a 6-digit code";
6303
+ setError(msg);
6304
+ onError?.(msg);
6305
+ return null;
6306
+ }
6307
+ setIsLoading(true);
6308
+ setError(null);
6309
+ setSetupStep("confirming");
6310
+ try {
6311
+ authLogger.info("Confirming 2FA setup...");
6312
+ const response = await apiTotp.totp_setup.confirmCreate({
6313
+ device_id: setupData.deviceId,
6314
+ code
6315
+ });
6316
+ const codes = response.backup_codes;
6317
+ setBackupCodes(codes);
6318
+ setBackupCodesWarning(response.backup_codes_warning);
6319
+ setSetupStep("complete");
6320
+ authLogger.info("2FA setup confirmed, backup codes generated:", codes.length);
6321
+ onComplete?.(codes);
6322
+ return codes;
6323
+ } catch (err) {
6324
+ const errorMessage = err instanceof Error ? err.message : "Invalid code. Please try again.";
6325
+ authLogger.error("2FA setup confirmation error:", err);
6326
+ setError(errorMessage);
6327
+ setSetupStep("scanning");
6328
+ onError?.(errorMessage);
6329
+ return null;
6330
+ } finally {
6331
+ setIsLoading(false);
6332
+ }
6333
+ }, [setupData, onComplete, onError]);
6334
+ return {
6335
+ isLoading,
6336
+ error,
6337
+ setupData,
6338
+ backupCodes,
6339
+ backupCodesWarning,
6340
+ setupStep,
6341
+ startSetup,
6342
+ confirmSetup,
6343
+ resetSetup,
6344
+ clearError
6345
+ };
6346
+ }, "useTwoFactorSetup");
6347
+
6348
+ // src/auth/hooks/useAuthGuard.ts
6349
+ import { useEffect as useEffect5, useState as useState8 } from "react";
6350
+ import { useCfgRouter as useCfgRouter5 } from "@djangocfg/ui-nextjs/hooks";
6351
+ var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
6352
+ const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
6353
+ const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
6354
+ const router = useCfgRouter5();
6355
+ const [isRedirecting, setIsRedirecting] = useState8(false);
6356
+ useEffect5(() => {
6357
+ if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
6358
+ if (shouldSaveUrl && typeof window !== "undefined") {
6359
+ const currentUrl = window.location.pathname + window.location.search;
6360
+ saveRedirectUrl(currentUrl);
6361
+ }
6362
+ setIsRedirecting(true);
6363
+ router.push(redirectTo);
6364
+ }
6365
+ }, [isAuthenticated, isLoading, router, redirectTo, requireAuth, isRedirecting, shouldSaveUrl, saveRedirectUrl]);
6366
+ return { isAuthenticated, isLoading, isRedirecting };
6367
+ }, "useAuthGuard");
6368
+
6369
+ // src/auth/hooks/useLocalStorage.ts
6370
+ import { useState as useState9 } from "react";
6371
+ function useLocalStorage2(key, initialValue) {
6372
+ const [storedValue, setStoredValue] = useState9(() => {
6373
+ if (typeof window === "undefined") {
6374
+ return initialValue;
6375
+ }
6376
+ try {
6377
+ const item = window.localStorage.getItem(key);
6378
+ if (item === null) {
6379
+ return initialValue;
6380
+ }
6381
+ try {
6382
+ return JSON.parse(item);
6383
+ } catch {
6384
+ return item;
6385
+ }
6386
+ } catch (error) {
6387
+ authLogger.error(`Error reading localStorage key "${key}":`, error);
6388
+ return initialValue;
6389
+ }
6390
+ });
6391
+ const checkDataSize = /* @__PURE__ */ __name((data) => {
6392
+ try {
6393
+ const jsonString = JSON.stringify(data);
6394
+ const sizeInBytes = new Blob([jsonString]).size;
6395
+ const sizeInKB = sizeInBytes / 1024;
6396
+ if (sizeInKB > 1024) {
6397
+ authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
6398
+ return false;
6399
+ }
6400
+ return true;
6401
+ } catch (error) {
6402
+ authLogger.error(`Error checking data size for key "${key}":`, error);
6403
+ return false;
6404
+ }
6405
+ }, "checkDataSize");
6406
+ const clearOldData = /* @__PURE__ */ __name(() => {
6407
+ try {
6408
+ const keys = Object.keys(localStorage).filter((key2) => key2 && typeof key2 === "string");
6409
+ if (keys.length > 50) {
6410
+ const itemsToRemove = Math.ceil(keys.length * 0.2);
6411
+ for (let i = 0; i < itemsToRemove; i++) {
6412
+ try {
6413
+ const key2 = keys[i];
6414
+ if (key2) {
6415
+ localStorage.removeItem(key2);
6416
+ localStorage.removeItem(`${key2}_timestamp`);
6417
+ }
6418
+ } catch {
6419
+ }
6420
+ }
6421
+ }
6422
+ } catch (error) {
6423
+ authLogger.error("Error clearing old localStorage data:", error);
6424
+ }
6425
+ }, "clearOldData");
6426
+ const forceClearAll = /* @__PURE__ */ __name(() => {
6427
+ try {
6428
+ const keys = Object.keys(localStorage);
6429
+ for (const key2 of keys) {
6430
+ try {
6431
+ localStorage.removeItem(key2);
6432
+ } catch {
6433
+ }
6434
+ }
6435
+ } catch (error) {
6436
+ authLogger.error("Error force clearing localStorage:", error);
6437
+ }
6438
+ }, "forceClearAll");
6439
+ const setValue = /* @__PURE__ */ __name((value) => {
6440
+ try {
6441
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
6442
+ if (!checkDataSize(valueToStore)) {
6443
+ authLogger.warn(`Data size too large for key "${key}", removing key`);
6444
+ try {
6445
+ window.localStorage.removeItem(key);
6446
+ window.localStorage.removeItem(`${key}_timestamp`);
6447
+ } catch {
6448
+ }
6449
+ setStoredValue(valueToStore);
6450
+ return;
6451
+ }
6452
+ setStoredValue(valueToStore);
6453
+ if (typeof window !== "undefined") {
6454
+ try {
6455
+ if (typeof valueToStore === "string") {
6456
+ window.localStorage.setItem(key, valueToStore);
6457
+ } else {
6458
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6459
+ }
6460
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6461
+ } catch (storageError) {
6462
+ if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
6463
+ authLogger.warn("localStorage quota exceeded, clearing old data...");
6464
+ clearOldData();
6465
+ try {
6466
+ if (typeof valueToStore === "string") {
6467
+ window.localStorage.setItem(key, valueToStore);
6468
+ } else {
6469
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6470
+ }
6471
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6472
+ } catch (retryError) {
6473
+ authLogger.error(`Failed to set localStorage key "${key}" after clearing old data:`, retryError);
6474
+ try {
6475
+ forceClearAll();
6476
+ if (typeof valueToStore === "string") {
6477
+ window.localStorage.setItem(key, valueToStore);
6478
+ } else {
6479
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6480
+ }
6481
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6482
+ } catch (finalError) {
6483
+ authLogger.error(`Failed to set localStorage key "${key}" after force clearing:`, finalError);
6484
+ setStoredValue(valueToStore);
6485
+ }
6486
+ }
6487
+ } else {
6488
+ throw storageError;
6489
+ }
6490
+ }
6491
+ }
6492
+ } catch (error) {
6493
+ authLogger.error(`Error setting localStorage key "${key}":`, error);
6494
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
6495
+ setStoredValue(valueToStore);
6496
+ }
6497
+ }, "setValue");
6498
+ const removeValue = /* @__PURE__ */ __name(() => {
6499
+ try {
6500
+ setStoredValue(initialValue);
6501
+ if (typeof window !== "undefined") {
6502
+ try {
6503
+ window.localStorage.removeItem(key);
6504
+ window.localStorage.removeItem(`${key}_timestamp`);
6505
+ } catch (removeError) {
6506
+ if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
6507
+ authLogger.warn("localStorage quota exceeded during removal, clearing old data...");
6508
+ clearOldData();
6509
+ try {
6510
+ window.localStorage.removeItem(key);
6511
+ window.localStorage.removeItem(`${key}_timestamp`);
6512
+ } catch (retryError) {
6513
+ authLogger.error(`Failed to remove localStorage key "${key}" after clearing:`, retryError);
6514
+ forceClearAll();
6515
+ }
6516
+ } else {
6517
+ throw removeError;
6518
+ }
6519
+ }
6520
+ }
6521
+ } catch (error) {
6522
+ authLogger.error(`Error removing localStorage key "${key}":`, error);
6523
+ }
6524
+ }, "removeValue");
6525
+ return [storedValue, setValue, removeValue];
6526
+ }
6527
+ __name(useLocalStorage2, "useLocalStorage");
6528
+
5072
6529
  // src/auth/utils/validation.ts
5073
6530
  var validateEmail = /* @__PURE__ */ __name((email) => {
5074
6531
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -5101,6 +6558,7 @@ export {
5101
6558
  authLogger,
5102
6559
  clearProfileCache,
5103
6560
  decodeBase64,
6561
+ detectChannelFromIdentifier,
5104
6562
  encodeBase64,
5105
6563
  formatAuthError,
5106
6564
  getCacheMetadata,
@@ -5111,13 +6569,18 @@ export {
5111
6569
  useAccountsContext,
5112
6570
  useAuth,
5113
6571
  useAuthForm,
6572
+ useAuthFormState,
5114
6573
  useAuthGuard,
5115
6574
  useAuthRedirectManager,
6575
+ useAuthValidation,
5116
6576
  useAutoAuth,
5117
6577
  useBase64,
5118
6578
  useGithubAuth,
5119
6579
  useLocalStorage2 as useLocalStorage,
5120
6580
  useSessionStorage,
5121
- validateEmail
6581
+ useTwoFactor,
6582
+ useTwoFactorSetup,
6583
+ validateEmail,
6584
+ validateIdentifier
5122
6585
  };
5123
6586
  //# sourceMappingURL=auth.mjs.map