@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.cjs CHANGED
@@ -39,6 +39,7 @@ __export(auth_exports, {
39
39
  authLogger: () => authLogger,
40
40
  clearProfileCache: () => clearProfileCache,
41
41
  decodeBase64: () => decodeBase64,
42
+ detectChannelFromIdentifier: () => detectChannelFromIdentifier,
42
43
  encodeBase64: () => encodeBase64,
43
44
  formatAuthError: () => formatAuthError,
44
45
  getCacheMetadata: () => getCacheMetadata,
@@ -49,14 +50,19 @@ __export(auth_exports, {
49
50
  useAccountsContext: () => useAccountsContext,
50
51
  useAuth: () => useAuth,
51
52
  useAuthForm: () => useAuthForm,
53
+ useAuthFormState: () => useAuthFormState,
52
54
  useAuthGuard: () => useAuthGuard,
53
55
  useAuthRedirectManager: () => useAuthRedirectManager,
56
+ useAuthValidation: () => useAuthValidation,
54
57
  useAutoAuth: () => useAutoAuth,
55
58
  useBase64: () => useBase64,
56
59
  useGithubAuth: () => useGithubAuth,
57
60
  useLocalStorage: () => useLocalStorage2,
58
61
  useSessionStorage: () => useSessionStorage,
59
- validateEmail: () => validateEmail
62
+ useTwoFactor: () => useTwoFactor,
63
+ useTwoFactorSetup: () => useTwoFactorSetup,
64
+ validateEmail: () => validateEmail,
65
+ validateIdentifier: () => validateIdentifier
60
66
  });
61
67
  module.exports = __toCommonJS(auth_exports);
62
68
 
@@ -222,7 +228,10 @@ var Accounts = class {
222
228
  return response;
223
229
  }
224
230
  /**
225
- * Verify OTP code and return JWT tokens.
231
+ * Verify OTP code and return JWT tokens or 2FA session. If user has 2FA
232
+ * enabled: - Returns requires_2fa=True with session_id - Client must
233
+ * complete 2FA verification at /cfg/totp/verify/ If user has no 2FA: -
234
+ * Returns JWT tokens and user data directly
226
235
  */
227
236
  async otpVerifyCreate(data) {
228
237
  const response = await this.client.request("POST", "/cfg/accounts/otp/verify/", { body: data });
@@ -236,7 +245,7 @@ var FetchAdapter = class {
236
245
  __name(this, "FetchAdapter");
237
246
  }
238
247
  async request(request) {
239
- const { method, url, headers, body, params, formData } = request;
248
+ const { method, url, headers, body, params, formData, binaryBody } = request;
240
249
  let finalUrl = url;
241
250
  if (params) {
242
251
  const searchParams = new URLSearchParams();
@@ -254,6 +263,9 @@ var FetchAdapter = class {
254
263
  let requestBody;
255
264
  if (formData) {
256
265
  requestBody = formData;
266
+ } else if (binaryBody) {
267
+ finalHeaders["Content-Type"] = "application/octet-stream";
268
+ requestBody = binaryBody;
257
269
  } else if (body) {
258
270
  finalHeaders["Content-Type"] = "application/json";
259
271
  requestBody = JSON.stringify(body);
@@ -653,7 +665,7 @@ var APIClient = class {
653
665
  const headers = {
654
666
  ...options?.headers || {}
655
667
  };
656
- if (!options?.formData && !headers["Content-Type"]) {
668
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
657
669
  headers["Content-Type"] = "application/json";
658
670
  }
659
671
  if (this.logger) {
@@ -672,7 +684,8 @@ var APIClient = class {
672
684
  headers,
673
685
  params: options?.params,
674
686
  body: options?.body,
675
- formData: options?.formData
687
+ formData: options?.formData,
688
+ binaryBody: options?.binaryBody
676
689
  });
677
690
  const duration = Date.now() - startTime;
678
691
  if (response.status >= 400) {
@@ -926,11 +939,14 @@ var OAuthProvidersResponseSchema = import_zod8.z.object({
926
939
  // src/generated/cfg_accounts/_utils/schemas/OAuthTokenResponse.schema.ts
927
940
  var import_zod9 = require("zod");
928
941
  var OAuthTokenResponseSchema = import_zod9.z.object({
929
- access: import_zod9.z.string(),
930
- refresh: import_zod9.z.string(),
931
- user: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.any()),
942
+ requires_2fa: import_zod9.z.boolean().optional(),
943
+ session_id: import_zod9.z.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(),
944
+ access: import_zod9.z.string().nullable().optional(),
945
+ refresh: import_zod9.z.string().nullable().optional(),
946
+ user: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.any()).nullable().optional(),
932
947
  is_new_user: import_zod9.z.boolean(),
933
- is_new_connection: import_zod9.z.boolean()
948
+ is_new_connection: import_zod9.z.boolean(),
949
+ should_prompt_2fa: import_zod9.z.boolean().optional()
934
950
  });
935
951
 
936
952
  // src/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts
@@ -989,9 +1005,12 @@ var UserSchema = import_zod14.z.object({
989
1005
 
990
1006
  // src/generated/cfg_accounts/_utils/schemas/OTPVerifyResponse.schema.ts
991
1007
  var OTPVerifyResponseSchema = import_zod15.z.object({
992
- refresh: import_zod15.z.string(),
993
- access: import_zod15.z.string(),
994
- user: UserSchema
1008
+ requires_2fa: import_zod15.z.boolean().optional(),
1009
+ session_id: import_zod15.z.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(),
1010
+ refresh: import_zod15.z.string().nullable().optional(),
1011
+ access: import_zod15.z.string().nullable().optional(),
1012
+ user: UserSchema.nullable().optional(),
1013
+ should_prompt_2fa: import_zod15.z.boolean().optional()
995
1014
  });
996
1015
 
997
1016
  // src/generated/cfg_accounts/_utils/schemas/PatchedUserProfileUpdateRequest.schema.ts
@@ -1710,7 +1729,7 @@ var FetchAdapter2 = class {
1710
1729
  __name(this, "FetchAdapter");
1711
1730
  }
1712
1731
  async request(request) {
1713
- const { method, url, headers, body, params, formData } = request;
1732
+ const { method, url, headers, body, params, formData, binaryBody } = request;
1714
1733
  let finalUrl = url;
1715
1734
  if (params) {
1716
1735
  const searchParams = new URLSearchParams();
@@ -1728,6 +1747,9 @@ var FetchAdapter2 = class {
1728
1747
  let requestBody;
1729
1748
  if (formData) {
1730
1749
  requestBody = formData;
1750
+ } else if (binaryBody) {
1751
+ finalHeaders["Content-Type"] = "application/octet-stream";
1752
+ requestBody = binaryBody;
1731
1753
  } else if (body) {
1732
1754
  finalHeaders["Content-Type"] = "application/json";
1733
1755
  requestBody = JSON.stringify(body);
@@ -2127,7 +2149,7 @@ var APIClient2 = class {
2127
2149
  const headers = {
2128
2150
  ...options?.headers || {}
2129
2151
  };
2130
- if (!options?.formData && !headers["Content-Type"]) {
2152
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
2131
2153
  headers["Content-Type"] = "application/json";
2132
2154
  }
2133
2155
  if (this.logger) {
@@ -2146,7 +2168,8 @@ var APIClient2 = class {
2146
2168
  headers,
2147
2169
  params: options?.params,
2148
2170
  body: options?.body,
2149
- formData: options?.formData
2171
+ formData: options?.formData,
2172
+ binaryBody: options?.binaryBody
2150
2173
  });
2151
2174
  const duration = Date.now() - startTime;
2152
2175
  if (response.status >= 400) {
@@ -2807,7 +2830,7 @@ var FetchAdapter3 = class {
2807
2830
  __name(this, "FetchAdapter");
2808
2831
  }
2809
2832
  async request(request) {
2810
- const { method, url, headers, body, params, formData } = request;
2833
+ const { method, url, headers, body, params, formData, binaryBody } = request;
2811
2834
  let finalUrl = url;
2812
2835
  if (params) {
2813
2836
  const searchParams = new URLSearchParams();
@@ -2825,6 +2848,9 @@ var FetchAdapter3 = class {
2825
2848
  let requestBody;
2826
2849
  if (formData) {
2827
2850
  requestBody = formData;
2851
+ } else if (binaryBody) {
2852
+ finalHeaders["Content-Type"] = "application/octet-stream";
2853
+ requestBody = binaryBody;
2828
2854
  } else if (body) {
2829
2855
  finalHeaders["Content-Type"] = "application/json";
2830
2856
  requestBody = JSON.stringify(body);
@@ -3221,7 +3247,7 @@ var APIClient3 = class {
3221
3247
  const headers = {
3222
3248
  ...options?.headers || {}
3223
3249
  };
3224
- if (!options?.formData && !headers["Content-Type"]) {
3250
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
3225
3251
  headers["Content-Type"] = "application/json";
3226
3252
  }
3227
3253
  if (this.logger) {
@@ -3240,7 +3266,8 @@ var APIClient3 = class {
3240
3266
  headers,
3241
3267
  params: options?.params,
3242
3268
  body: options?.body,
3243
- formData: options?.formData
3269
+ formData: options?.formData,
3270
+ binaryBody: options?.binaryBody
3244
3271
  });
3245
3272
  const duration = Date.now() - startTime;
3246
3273
  if (response.status >= 400) {
@@ -4110,6 +4137,10 @@ function AccountsProvider({ children }) {
4110
4137
  }, "requestOTP");
4111
4138
  const verifyOTP = /* @__PURE__ */ __name(async (data) => {
4112
4139
  const result = await otpVerifyMutation(data, api);
4140
+ if (result.requires_2fa && result.session_id) {
4141
+ authLogger.info("2FA required, session:", result.session_id);
4142
+ return result;
4143
+ }
4113
4144
  if (result.access && result.refresh) {
4114
4145
  api.setToken(result.access, result.refresh);
4115
4146
  await refreshProfile("AccountsContext.verifyOTP");
@@ -4368,7 +4399,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4368
4399
  [accounts]
4369
4400
  );
4370
4401
  const verifyOTP = (0, import_react3.useCallback)(
4371
- async (identifier, otpCode, channel, sourceUrl, redirectUrl) => {
4402
+ async (identifier, otpCode, channel, sourceUrl, redirectUrl, skipRedirect) => {
4372
4403
  try {
4373
4404
  const channelValue = channel === "phone" ? enums_exports.OTPVerifyRequestChannel.PHONE : enums_exports.OTPVerifyRequestChannel.EMAIL;
4374
4405
  const result = await accounts.verifyOTP({
@@ -4376,6 +4407,16 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4376
4407
  otp: otpCode,
4377
4408
  channel: channelValue
4378
4409
  });
4410
+ if (result.requires_2fa && result.session_id) {
4411
+ authLogger.info("2FA required, session:", result.session_id);
4412
+ return {
4413
+ success: true,
4414
+ message: "OTP verified, 2FA required",
4415
+ requires_2fa: true,
4416
+ session_id: result.session_id,
4417
+ should_prompt_2fa: result.should_prompt_2fa
4418
+ };
4419
+ }
4379
4420
  if (!result.access || !result.refresh) {
4380
4421
  authLogger.error("Verify OTP returned invalid response:", result);
4381
4422
  return {
@@ -4398,14 +4439,17 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4398
4439
  if (result.user?.id) {
4399
4440
  Analytics.setUser(String(result.user.id));
4400
4441
  }
4401
- const savedRedirect = redirectManager.useAndClearRedirect();
4402
- const finalRedirectUrl = redirectUrl || savedRedirect || config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
4403
- authLogger.info("Redirecting after auth to:", finalRedirectUrl);
4404
- router.hardPush(finalRedirectUrl);
4442
+ if (!skipRedirect) {
4443
+ const savedRedirect = redirectManager.useAndClearRedirect();
4444
+ const finalRedirectUrl = redirectUrl || savedRedirect || config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
4445
+ authLogger.info("Redirecting after auth to:", finalRedirectUrl);
4446
+ router.hardPush(finalRedirectUrl);
4447
+ }
4405
4448
  return {
4406
4449
  success: true,
4407
4450
  message: "Login successful",
4408
- user: result.user
4451
+ user: result.user,
4452
+ should_prompt_2fa: result.should_prompt_2fa
4409
4453
  };
4410
4454
  } catch (error) {
4411
4455
  authLogger.error("Verify OTP error:", error);
@@ -4545,199 +4589,107 @@ var useAuth = /* @__PURE__ */ __name(() => {
4545
4589
  return context;
4546
4590
  }, "useAuth");
4547
4591
 
4548
- // src/auth/hooks/useAuthGuard.ts
4592
+ // src/auth/hooks/useAuthFormState.ts
4549
4593
  var import_react4 = require("react");
4550
- var import_hooks3 = require("@djangocfg/ui-nextjs/hooks");
4551
- var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
4552
- const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
4553
- const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
4554
- const router = (0, import_hooks3.useCfgRouter)();
4555
- const [isRedirecting, setIsRedirecting] = (0, import_react4.useState)(false);
4556
- (0, import_react4.useEffect)(() => {
4557
- if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
4558
- if (shouldSaveUrl && typeof window !== "undefined") {
4559
- const currentUrl = window.location.pathname + window.location.search;
4560
- saveRedirectUrl(currentUrl);
4561
- }
4562
- setIsRedirecting(true);
4563
- router.push(redirectTo);
4564
- }
4565
- }, [isAuthenticated, isLoading, router, redirectTo, requireAuth, isRedirecting, shouldSaveUrl, saveRedirectUrl]);
4566
- return { isAuthenticated, isLoading, isRedirecting };
4567
- }, "useAuthGuard");
4594
+ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialChannel = "email") => {
4595
+ const [identifier, setIdentifier] = (0, import_react4.useState)(initialIdentifier);
4596
+ const [channel, setChannel] = (0, import_react4.useState)(initialChannel);
4597
+ const [otp, setOtp] = (0, import_react4.useState)("");
4598
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
4599
+ const [acceptedTerms, setAcceptedTerms] = (0, import_react4.useState)(false);
4600
+ const [step, setStep] = (0, import_react4.useState)("identifier");
4601
+ const [error, setError] = (0, import_react4.useState)("");
4602
+ const [twoFactorSessionId, setTwoFactorSessionId] = (0, import_react4.useState)(null);
4603
+ const [shouldPrompt2FA, setShouldPrompt2FA] = (0, import_react4.useState)(false);
4604
+ const [twoFactorCode, setTwoFactorCode] = (0, import_react4.useState)("");
4605
+ const [useBackupCode, setUseBackupCode] = (0, import_react4.useState)(false);
4606
+ const clearError = (0, import_react4.useCallback)(() => setError(""), []);
4607
+ return {
4608
+ // State
4609
+ identifier,
4610
+ channel,
4611
+ otp,
4612
+ isLoading,
4613
+ acceptedTerms,
4614
+ step,
4615
+ error,
4616
+ twoFactorSessionId,
4617
+ shouldPrompt2FA,
4618
+ twoFactorCode,
4619
+ useBackupCode,
4620
+ // Handlers
4621
+ setIdentifier,
4622
+ setChannel,
4623
+ setOtp,
4624
+ setAcceptedTerms,
4625
+ setError,
4626
+ clearError,
4627
+ setStep,
4628
+ setIsLoading,
4629
+ setTwoFactorSessionId,
4630
+ setShouldPrompt2FA,
4631
+ setTwoFactorCode,
4632
+ setUseBackupCode
4633
+ };
4634
+ }, "useAuthFormState");
4568
4635
 
4569
- // src/auth/hooks/useLocalStorage.ts
4636
+ // src/auth/hooks/useAuthValidation.ts
4570
4637
  var import_react5 = require("react");
4571
- function useLocalStorage2(key, initialValue) {
4572
- const [storedValue, setStoredValue] = (0, import_react5.useState)(() => {
4573
- if (typeof window === "undefined") {
4574
- return initialValue;
4575
- }
4576
- try {
4577
- const item = window.localStorage.getItem(key);
4578
- if (item === null) {
4579
- return initialValue;
4580
- }
4581
- try {
4582
- return JSON.parse(item);
4583
- } catch {
4584
- return item;
4585
- }
4586
- } catch (error) {
4587
- authLogger.error(`Error reading localStorage key "${key}":`, error);
4588
- return initialValue;
4589
- }
4590
- });
4591
- const checkDataSize = /* @__PURE__ */ __name((data) => {
4592
- try {
4593
- const jsonString = JSON.stringify(data);
4594
- const sizeInBytes = new Blob([jsonString]).size;
4595
- const sizeInKB = sizeInBytes / 1024;
4596
- if (sizeInKB > 1024) {
4597
- authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
4598
- return false;
4599
- }
4600
- return true;
4601
- } catch (error) {
4602
- authLogger.error(`Error checking data size for key "${key}":`, error);
4603
- return false;
4604
- }
4605
- }, "checkDataSize");
4606
- const clearOldData = /* @__PURE__ */ __name(() => {
4607
- try {
4608
- const keys = Object.keys(localStorage).filter((key2) => key2 && typeof key2 === "string");
4609
- if (keys.length > 50) {
4610
- const itemsToRemove = Math.ceil(keys.length * 0.2);
4611
- for (let i = 0; i < itemsToRemove; i++) {
4612
- try {
4613
- const key2 = keys[i];
4614
- if (key2) {
4615
- localStorage.removeItem(key2);
4616
- localStorage.removeItem(`${key2}_timestamp`);
4617
- }
4618
- } catch {
4619
- }
4620
- }
4621
- }
4622
- } catch (error) {
4623
- authLogger.error("Error clearing old localStorage data:", error);
4638
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4639
+ var PHONE_REGEX = /^\+[1-9]\d{6,14}$/;
4640
+ var useAuthValidation = /* @__PURE__ */ __name(() => {
4641
+ const detectChannelFromIdentifier2 = (0, import_react5.useCallback)((id) => {
4642
+ if (!id) return null;
4643
+ if (id.includes("@")) {
4644
+ return "email";
4624
4645
  }
4625
- }, "clearOldData");
4626
- const forceClearAll = /* @__PURE__ */ __name(() => {
4627
- try {
4628
- const keys = Object.keys(localStorage);
4629
- for (const key2 of keys) {
4630
- try {
4631
- localStorage.removeItem(key2);
4632
- } catch {
4633
- }
4634
- }
4635
- } catch (error) {
4636
- authLogger.error("Error force clearing localStorage:", error);
4646
+ if (id.startsWith("+") && PHONE_REGEX.test(id)) {
4647
+ return "phone";
4637
4648
  }
4638
- }, "forceClearAll");
4639
- const setValue = /* @__PURE__ */ __name((value) => {
4640
- try {
4641
- const valueToStore = value instanceof Function ? value(storedValue) : value;
4642
- if (!checkDataSize(valueToStore)) {
4643
- authLogger.warn(`Data size too large for key "${key}", removing key`);
4644
- try {
4645
- window.localStorage.removeItem(key);
4646
- window.localStorage.removeItem(`${key}_timestamp`);
4647
- } catch {
4648
- }
4649
- setStoredValue(valueToStore);
4650
- return;
4651
- }
4652
- setStoredValue(valueToStore);
4653
- if (typeof window !== "undefined") {
4654
- try {
4655
- if (typeof valueToStore === "string") {
4656
- window.localStorage.setItem(key, valueToStore);
4657
- } else {
4658
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4659
- }
4660
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4661
- } catch (storageError) {
4662
- if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
4663
- authLogger.warn("localStorage quota exceeded, clearing old data...");
4664
- clearOldData();
4665
- try {
4666
- if (typeof valueToStore === "string") {
4667
- window.localStorage.setItem(key, valueToStore);
4668
- } else {
4669
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4670
- }
4671
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4672
- } catch (retryError) {
4673
- authLogger.error(`Failed to set localStorage key "${key}" after clearing old data:`, retryError);
4674
- try {
4675
- forceClearAll();
4676
- if (typeof valueToStore === "string") {
4677
- window.localStorage.setItem(key, valueToStore);
4678
- } else {
4679
- window.localStorage.setItem(key, JSON.stringify(valueToStore));
4680
- }
4681
- window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
4682
- } catch (finalError) {
4683
- authLogger.error(`Failed to set localStorage key "${key}" after force clearing:`, finalError);
4684
- setStoredValue(valueToStore);
4685
- }
4686
- }
4687
- } else {
4688
- throw storageError;
4689
- }
4690
- }
4691
- }
4692
- } catch (error) {
4693
- authLogger.error(`Error setting localStorage key "${key}":`, error);
4694
- const valueToStore = value instanceof Function ? value(storedValue) : value;
4695
- setStoredValue(valueToStore);
4649
+ return null;
4650
+ }, []);
4651
+ const validateIdentifier2 = (0, import_react5.useCallback)((id, channelType) => {
4652
+ if (!id) return false;
4653
+ const channel = channelType || detectChannelFromIdentifier2(id);
4654
+ if (channel === "email") {
4655
+ return EMAIL_REGEX.test(id);
4696
4656
  }
4697
- }, "setValue");
4698
- const removeValue = /* @__PURE__ */ __name(() => {
4699
- try {
4700
- setStoredValue(initialValue);
4701
- if (typeof window !== "undefined") {
4702
- try {
4703
- window.localStorage.removeItem(key);
4704
- window.localStorage.removeItem(`${key}_timestamp`);
4705
- } catch (removeError) {
4706
- if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
4707
- authLogger.warn("localStorage quota exceeded during removal, clearing old data...");
4708
- clearOldData();
4709
- try {
4710
- window.localStorage.removeItem(key);
4711
- window.localStorage.removeItem(`${key}_timestamp`);
4712
- } catch (retryError) {
4713
- authLogger.error(`Failed to remove localStorage key "${key}" after clearing:`, retryError);
4714
- forceClearAll();
4715
- }
4716
- } else {
4717
- throw removeError;
4718
- }
4719
- }
4720
- }
4721
- } catch (error) {
4722
- authLogger.error(`Error removing localStorage key "${key}":`, error);
4657
+ if (channel === "phone") {
4658
+ return PHONE_REGEX.test(id);
4723
4659
  }
4724
- }, "removeValue");
4725
- return [storedValue, setValue, removeValue];
4726
- }
4727
- __name(useLocalStorage2, "useLocalStorage");
4660
+ return false;
4661
+ }, [detectChannelFromIdentifier2]);
4662
+ return {
4663
+ detectChannelFromIdentifier: detectChannelFromIdentifier2,
4664
+ validateIdentifier: validateIdentifier2
4665
+ };
4666
+ }, "useAuthValidation");
4667
+ var detectChannelFromIdentifier = /* @__PURE__ */ __name((id) => {
4668
+ if (!id) return null;
4669
+ if (id.includes("@")) return "email";
4670
+ if (id.startsWith("+") && PHONE_REGEX.test(id)) return "phone";
4671
+ return null;
4672
+ }, "detectChannelFromIdentifier");
4673
+ var validateIdentifier = /* @__PURE__ */ __name((id, channelType) => {
4674
+ if (!id) return false;
4675
+ const channel = channelType || detectChannelFromIdentifier(id);
4676
+ if (channel === "email") return EMAIL_REGEX.test(id);
4677
+ if (channel === "phone") return PHONE_REGEX.test(id);
4678
+ return false;
4679
+ }, "validateIdentifier");
4728
4680
 
4729
4681
  // src/auth/hooks/useAuthForm.ts
4730
- var import_react7 = require("react");
4682
+ var import_react8 = require("react");
4731
4683
 
4732
4684
  // src/auth/hooks/useAutoAuth.ts
4733
4685
  var import_navigation2 = require("next/navigation");
4734
4686
  var import_react6 = require("react");
4735
- var import_hooks4 = require("@djangocfg/ui-nextjs/hooks");
4687
+ var import_hooks3 = require("@djangocfg/ui-nextjs/hooks");
4736
4688
  var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
4737
4689
  const { onOTPDetected, cleanupUrl = true, allowedPaths = ["/auth"] } = options;
4738
- const queryParams = (0, import_hooks4.useQueryParams)();
4690
+ const queryParams = (0, import_hooks3.useQueryParams)();
4739
4691
  const pathname = (0, import_navigation2.usePathname)();
4740
- const router = (0, import_hooks4.useCfgRouter)();
4692
+ const router = (0, import_hooks3.useCfgRouter)();
4741
4693
  const isAllowedPath = allowedPaths.some((path) => pathname === path || pathname?.startsWith(path + "/"));
4742
4694
  const hasOTP = !!queryParams.get("otp");
4743
4695
  const isReady = !!pathname && hasOTP && isAllowedPath;
@@ -4762,81 +4714,1275 @@ var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
4762
4714
  };
4763
4715
  }, "useAutoAuth");
4764
4716
 
4765
- // src/auth/hooks/useAuthForm.ts
4766
- var useAuthForm = /* @__PURE__ */ __name((options) => {
4767
- const { onIdentifierSuccess, onOTPSuccess, onError, sourceUrl, redirectUrl, requireTermsAcceptance = false, authPath = "/auth" } = options;
4768
- const [identifier, setIdentifier] = (0, import_react7.useState)("");
4769
- const [channel, setChannel] = (0, import_react7.useState)("email");
4770
- const [otp, setOtp] = (0, import_react7.useState)("");
4771
- const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
4772
- const [acceptedTerms, setAcceptedTerms] = (0, import_react7.useState)(false);
4773
- const [step, setStep] = (0, import_react7.useState)("identifier");
4774
- const [error, setError] = (0, import_react7.useState)("");
4775
- const { requestOTP, verifyOTP, getSavedEmail, saveEmail, getSavedPhone, savePhone } = useAuth();
4776
- const [savedTermsAccepted, setSavedTermsAccepted] = useLocalStorage2("auth_terms_accepted", false);
4777
- const [savedEmail, setSavedEmail] = useLocalStorage2("auth_email", "");
4778
- const [savedPhone, setSavedPhone] = useLocalStorage2("auth_phone", "");
4779
- const detectChannelFromIdentifier = (0, import_react7.useCallback)((identifier2) => {
4780
- if (!identifier2) return null;
4781
- if (identifier2.includes("@")) {
4782
- return "email";
4783
- }
4784
- if (identifier2.startsWith("+") && /^\+[1-9]\d{6,14}$/.test(identifier2)) {
4785
- return "phone";
4786
- }
4787
- return null;
4788
- }, []);
4789
- const validateIdentifier = (0, import_react7.useCallback)((identifier2, channelType) => {
4790
- if (!identifier2) return false;
4791
- const detectedChannel = channelType || detectChannelFromIdentifier(identifier2);
4792
- if (detectedChannel === "email") {
4793
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(identifier2);
4794
- } else if (detectedChannel === "phone") {
4795
- return /^\+[1-9]\d{6,14}$/.test(identifier2);
4796
- }
4797
- return false;
4798
- }, [detectChannelFromIdentifier]);
4799
- (0, import_react7.useEffect)(() => {
4800
- const authSavedEmail = getSavedEmail();
4801
- const authSavedPhone = getSavedPhone();
4802
- if (authSavedPhone) {
4803
- setIdentifier(authSavedPhone);
4804
- setChannel("phone");
4805
- } else if (authSavedEmail) {
4806
- setIdentifier(authSavedEmail);
4807
- setChannel("email");
4808
- }
4809
- if (savedTermsAccepted) {
4810
- setAcceptedTerms(savedTermsAccepted);
4811
- }
4812
- }, [getSavedEmail, getSavedPhone, savedTermsAccepted]);
4813
- (0, import_react7.useEffect)(() => {
4814
- if (identifier) {
4815
- const detectedChannel = detectChannelFromIdentifier(identifier);
4816
- if (detectedChannel && detectedChannel !== channel) {
4817
- setChannel(detectedChannel);
4818
- }
4819
- }
4820
- }, [identifier, channel, detectChannelFromIdentifier]);
4821
- const clearError = (0, import_react7.useCallback)(() => setError(""), []);
4822
- const handleIdentifierSubmit = (0, import_react7.useCallback)(async (e) => {
4823
- e.preventDefault();
4824
- if (!identifier) {
4825
- const message = channel === "phone" ? "Please enter your phone number" : "Please enter your email address";
4826
- setError(message);
4827
- onError?.(message);
4828
- return;
4717
+ // src/auth/hooks/useTwoFactor.ts
4718
+ var import_react7 = require("react");
4719
+ var import_hooks4 = require("@djangocfg/ui-nextjs/hooks");
4720
+
4721
+ // src/generated/cfg_totp/totp__backup_codes/client.ts
4722
+ var BackupCodes = class {
4723
+ static {
4724
+ __name(this, "BackupCodes");
4725
+ }
4726
+ constructor(client) {
4727
+ this.client = client;
4728
+ }
4729
+ /**
4730
+ * Get backup codes status for user.
4731
+ */
4732
+ async totpBackupCodesRetrieve() {
4733
+ const response = await this.client.request("GET", "/cfg/totp/backup-codes/");
4734
+ return response;
4735
+ }
4736
+ /**
4737
+ * Regenerate backup codes. Requires TOTP code for verification.
4738
+ * Invalidates all existing codes.
4739
+ */
4740
+ async totpBackupCodesRegenerateCreate(data) {
4741
+ const response = await this.client.request("POST", "/cfg/totp/backup-codes/regenerate/", { body: data });
4742
+ return response;
4743
+ }
4744
+ };
4745
+
4746
+ // src/generated/cfg_totp/totp__totp_management/client.ts
4747
+ var TotpManagement = class {
4748
+ static {
4749
+ __name(this, "TotpManagement");
4750
+ }
4751
+ constructor(client) {
4752
+ this.client = client;
4753
+ }
4754
+ /**
4755
+ * List all TOTP devices for user.
4756
+ */
4757
+ async totpDevicesList(...args) {
4758
+ const isParamsObject = args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0]);
4759
+ let params;
4760
+ if (isParamsObject) {
4761
+ params = args[0];
4762
+ } else {
4763
+ params = { page: args[0], page_size: args[1] };
4829
4764
  }
4830
- if (!validateIdentifier(identifier, channel)) {
4831
- const message = channel === "phone" ? "Please enter a valid phone number (e.g., +1234567890)" : "Please enter a valid email address";
4832
- setError(message);
4833
- onError?.(message);
4834
- return;
4765
+ const response = await this.client.request("GET", "/cfg/totp/devices/", { params });
4766
+ return response;
4767
+ }
4768
+ /**
4769
+ * Completely disable 2FA for account. Requires verification code.
4770
+ */
4771
+ async totpDisableCreate(data) {
4772
+ const response = await this.client.request("POST", "/cfg/totp/disable/", { body: data });
4773
+ return response;
4774
+ }
4775
+ };
4776
+
4777
+ // src/generated/cfg_totp/totp__totp_setup/client.ts
4778
+ var TotpSetup = class {
4779
+ static {
4780
+ __name(this, "TotpSetup");
4781
+ }
4782
+ constructor(client) {
4783
+ this.client = client;
4784
+ }
4785
+ /**
4786
+ * Start 2FA setup process. Creates a new TOTP device and returns QR code
4787
+ * for scanning.
4788
+ */
4789
+ async create(data) {
4790
+ const response = await this.client.request("POST", "/cfg/totp/setup/", { body: data });
4791
+ return response;
4792
+ }
4793
+ /**
4794
+ * Confirm 2FA setup with first valid code. Activates the device and
4795
+ * generates backup codes.
4796
+ */
4797
+ async confirmCreate(data) {
4798
+ const response = await this.client.request("POST", "/cfg/totp/setup/confirm/", { body: data });
4799
+ return response;
4800
+ }
4801
+ };
4802
+
4803
+ // src/generated/cfg_totp/totp__totp_verification/client.ts
4804
+ var TotpVerification = class {
4805
+ static {
4806
+ __name(this, "TotpVerification");
4807
+ }
4808
+ constructor(client) {
4809
+ this.client = client;
4810
+ }
4811
+ /**
4812
+ * Verify TOTP code for 2FA session. Completes authentication and returns
4813
+ * JWT tokens on success.
4814
+ */
4815
+ async totpVerifyCreate(data) {
4816
+ const response = await this.client.request("POST", "/cfg/totp/verify/", { body: data });
4817
+ return response;
4818
+ }
4819
+ /**
4820
+ * Verify backup recovery code for 2FA session. Alternative verification
4821
+ * method when TOTP device unavailable.
4822
+ */
4823
+ async totpVerifyBackupCreate(data) {
4824
+ const response = await this.client.request("POST", "/cfg/totp/verify/backup/", { body: data });
4825
+ return response;
4826
+ }
4827
+ };
4828
+
4829
+ // src/generated/cfg_totp/totp/client.ts
4830
+ var Totp = class {
4831
+ static {
4832
+ __name(this, "Totp");
4833
+ }
4834
+ constructor(client) {
4835
+ this.client = client;
4836
+ }
4837
+ /**
4838
+ * Delete a TOTP device. Requires verification code if removing the
4839
+ * last/primary device.
4840
+ */
4841
+ async devicesDestroy(id) {
4842
+ const response = await this.client.request("DELETE", `/cfg/totp/devices/${id}/`);
4843
+ return;
4844
+ }
4845
+ };
4846
+
4847
+ // src/generated/cfg_totp/http.ts
4848
+ var FetchAdapter4 = class {
4849
+ static {
4850
+ __name(this, "FetchAdapter");
4851
+ }
4852
+ async request(request) {
4853
+ const { method, url, headers, body, params, formData, binaryBody } = request;
4854
+ let finalUrl = url;
4855
+ if (params) {
4856
+ const searchParams = new URLSearchParams();
4857
+ Object.entries(params).forEach(([key, value]) => {
4858
+ if (value !== null && value !== void 0) {
4859
+ searchParams.append(key, String(value));
4860
+ }
4861
+ });
4862
+ const queryString = searchParams.toString();
4863
+ if (queryString) {
4864
+ finalUrl = url.includes("?") ? `${url}&${queryString}` : `${url}?${queryString}`;
4865
+ }
4866
+ }
4867
+ const finalHeaders = { ...headers };
4868
+ let requestBody;
4869
+ if (formData) {
4870
+ requestBody = formData;
4871
+ } else if (binaryBody) {
4872
+ finalHeaders["Content-Type"] = "application/octet-stream";
4873
+ requestBody = binaryBody;
4874
+ } else if (body) {
4875
+ finalHeaders["Content-Type"] = "application/json";
4876
+ requestBody = JSON.stringify(body);
4877
+ }
4878
+ const response = await fetch(finalUrl, {
4879
+ method,
4880
+ headers: finalHeaders,
4881
+ body: requestBody,
4882
+ credentials: "include"
4883
+ // Include Django session cookies
4884
+ });
4885
+ let data = null;
4886
+ const contentType = response.headers.get("content-type");
4887
+ if (response.status !== 204 && contentType?.includes("application/json")) {
4888
+ data = await response.json();
4889
+ } else if (response.status !== 204) {
4890
+ data = await response.text();
4891
+ }
4892
+ const responseHeaders = {};
4893
+ response.headers.forEach((value, key) => {
4894
+ responseHeaders[key] = value;
4895
+ });
4896
+ return {
4897
+ data,
4898
+ status: response.status,
4899
+ statusText: response.statusText,
4900
+ headers: responseHeaders
4901
+ };
4902
+ }
4903
+ };
4904
+
4905
+ // src/generated/cfg_totp/errors.ts
4906
+ var APIError4 = class extends Error {
4907
+ constructor(statusCode, statusText, response, url, message) {
4908
+ super(message || `HTTP ${statusCode}: ${statusText}`);
4909
+ this.statusCode = statusCode;
4910
+ this.statusText = statusText;
4911
+ this.response = response;
4912
+ this.url = url;
4913
+ this.name = "APIError";
4914
+ }
4915
+ static {
4916
+ __name(this, "APIError");
4917
+ }
4918
+ /**
4919
+ * Get error details from response.
4920
+ * DRF typically returns: { "detail": "Error message" } or { "field": ["error1", "error2"] }
4921
+ */
4922
+ get details() {
4923
+ if (typeof this.response === "object" && this.response !== null) {
4924
+ return this.response;
4925
+ }
4926
+ return null;
4927
+ }
4928
+ /**
4929
+ * Get field-specific validation errors from DRF.
4930
+ * Returns: { "field_name": ["error1", "error2"], ... }
4931
+ */
4932
+ get fieldErrors() {
4933
+ const details = this.details;
4934
+ if (!details) return null;
4935
+ const fieldErrors = {};
4936
+ for (const [key, value] of Object.entries(details)) {
4937
+ if (Array.isArray(value)) {
4938
+ fieldErrors[key] = value;
4939
+ }
4940
+ }
4941
+ return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
4942
+ }
4943
+ /**
4944
+ * Get single error message from DRF.
4945
+ * Checks for "detail", "message", or first field error.
4946
+ */
4947
+ get errorMessage() {
4948
+ const details = this.details;
4949
+ if (!details) return this.message;
4950
+ if (details.detail) {
4951
+ return Array.isArray(details.detail) ? details.detail.join(", ") : String(details.detail);
4952
+ }
4953
+ if (details.message) {
4954
+ return String(details.message);
4955
+ }
4956
+ const fieldErrors = this.fieldErrors;
4957
+ if (fieldErrors) {
4958
+ const firstField = Object.keys(fieldErrors)[0];
4959
+ if (firstField) {
4960
+ return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
4961
+ }
4962
+ }
4963
+ return this.message;
4964
+ }
4965
+ // Helper methods for common HTTP status codes
4966
+ get isValidationError() {
4967
+ return this.statusCode === 400;
4968
+ }
4969
+ get isAuthError() {
4970
+ return this.statusCode === 401;
4971
+ }
4972
+ get isPermissionError() {
4973
+ return this.statusCode === 403;
4974
+ }
4975
+ get isNotFoundError() {
4976
+ return this.statusCode === 404;
4977
+ }
4978
+ get isServerError() {
4979
+ return this.statusCode >= 500 && this.statusCode < 600;
4980
+ }
4981
+ };
4982
+ var NetworkError4 = class extends Error {
4983
+ constructor(message, url, originalError) {
4984
+ super(message);
4985
+ this.url = url;
4986
+ this.originalError = originalError;
4987
+ this.name = "NetworkError";
4988
+ }
4989
+ static {
4990
+ __name(this, "NetworkError");
4991
+ }
4992
+ };
4993
+
4994
+ // src/generated/cfg_totp/logger.ts
4995
+ var import_consola14 = require("consola");
4996
+ var DEFAULT_CONFIG4 = {
4997
+ enabled: process.env.NODE_ENV !== "production",
4998
+ logRequests: true,
4999
+ logResponses: true,
5000
+ logErrors: true,
5001
+ logBodies: true,
5002
+ logHeaders: false
5003
+ };
5004
+ var SENSITIVE_HEADERS4 = [
5005
+ "authorization",
5006
+ "cookie",
5007
+ "set-cookie",
5008
+ "x-api-key",
5009
+ "x-csrf-token"
5010
+ ];
5011
+ var APILogger4 = class {
5012
+ static {
5013
+ __name(this, "APILogger");
5014
+ }
5015
+ constructor(config = {}) {
5016
+ this.config = { ...DEFAULT_CONFIG4, ...config };
5017
+ this.consola = config.consola || (0, import_consola14.createConsola)({
5018
+ level: this.config.enabled ? 4 : 0
5019
+ });
5020
+ }
5021
+ /**
5022
+ * Enable logging
5023
+ */
5024
+ enable() {
5025
+ this.config.enabled = true;
5026
+ }
5027
+ /**
5028
+ * Disable logging
5029
+ */
5030
+ disable() {
5031
+ this.config.enabled = false;
5032
+ }
5033
+ /**
5034
+ * Update configuration
5035
+ */
5036
+ setConfig(config) {
5037
+ this.config = { ...this.config, ...config };
5038
+ }
5039
+ /**
5040
+ * Filter sensitive headers
5041
+ */
5042
+ filterHeaders(headers) {
5043
+ if (!headers) return {};
5044
+ const filtered = {};
5045
+ Object.keys(headers).forEach((key) => {
5046
+ const lowerKey = key.toLowerCase();
5047
+ if (SENSITIVE_HEADERS4.includes(lowerKey)) {
5048
+ filtered[key] = "***";
5049
+ } else {
5050
+ filtered[key] = headers[key] || "";
5051
+ }
5052
+ });
5053
+ return filtered;
5054
+ }
5055
+ /**
5056
+ * Log request
5057
+ */
5058
+ logRequest(request) {
5059
+ if (!this.config.enabled || !this.config.logRequests) return;
5060
+ const { method, url, headers, body } = request;
5061
+ this.consola.start(`${method} ${url}`);
5062
+ if (this.config.logHeaders && headers) {
5063
+ this.consola.debug("Headers:", this.filterHeaders(headers));
5064
+ }
5065
+ if (this.config.logBodies && body) {
5066
+ this.consola.debug("Body:", body);
5067
+ }
5068
+ }
5069
+ /**
5070
+ * Log response
5071
+ */
5072
+ logResponse(request, response) {
5073
+ if (!this.config.enabled || !this.config.logResponses) return;
5074
+ const { method, url } = request;
5075
+ const { status, statusText, data, duration } = response;
5076
+ const statusColor = status >= 500 ? "red" : status >= 400 ? "yellow" : status >= 300 ? "cyan" : "green";
5077
+ this.consola.success(
5078
+ `${method} ${url} ${status} ${statusText} (${duration}ms)`
5079
+ );
5080
+ if (this.config.logBodies && data) {
5081
+ this.consola.debug("Response:", data);
5082
+ }
5083
+ }
5084
+ /**
5085
+ * Log error
5086
+ */
5087
+ logError(request, error) {
5088
+ if (!this.config.enabled || !this.config.logErrors) return;
5089
+ const { method, url } = request;
5090
+ const { message, statusCode, fieldErrors, duration } = error;
5091
+ this.consola.error(
5092
+ `${method} ${url} ${statusCode || "Network"} Error (${duration}ms)`
5093
+ );
5094
+ this.consola.error("Message:", message);
5095
+ if (fieldErrors && Object.keys(fieldErrors).length > 0) {
5096
+ this.consola.error("Field Errors:");
5097
+ Object.entries(fieldErrors).forEach(([field, errors]) => {
5098
+ errors.forEach((err) => {
5099
+ this.consola.error(` \u2022 ${field}: ${err}`);
5100
+ });
5101
+ });
5102
+ }
5103
+ }
5104
+ /**
5105
+ * Log general info
5106
+ */
5107
+ info(message, ...args) {
5108
+ if (!this.config.enabled) return;
5109
+ this.consola.info(message, ...args);
5110
+ }
5111
+ /**
5112
+ * Log warning
5113
+ */
5114
+ warn(message, ...args) {
5115
+ if (!this.config.enabled) return;
5116
+ this.consola.warn(message, ...args);
5117
+ }
5118
+ /**
5119
+ * Log error
5120
+ */
5121
+ error(message, ...args) {
5122
+ if (!this.config.enabled) return;
5123
+ this.consola.error(message, ...args);
5124
+ }
5125
+ /**
5126
+ * Log debug
5127
+ */
5128
+ debug(message, ...args) {
5129
+ if (!this.config.enabled) return;
5130
+ this.consola.debug(message, ...args);
5131
+ }
5132
+ /**
5133
+ * Log success
5134
+ */
5135
+ success(message, ...args) {
5136
+ if (!this.config.enabled) return;
5137
+ this.consola.success(message, ...args);
5138
+ }
5139
+ /**
5140
+ * Create a sub-logger with prefix
5141
+ */
5142
+ withTag(tag) {
5143
+ return this.consola.withTag(tag);
5144
+ }
5145
+ };
5146
+ var defaultLogger4 = new APILogger4();
5147
+
5148
+ // src/generated/cfg_totp/retry.ts
5149
+ var import_p_retry4 = __toESM(require("p-retry"), 1);
5150
+ var DEFAULT_RETRY_CONFIG4 = {
5151
+ retries: 3,
5152
+ factor: 2,
5153
+ minTimeout: 1e3,
5154
+ maxTimeout: 6e4,
5155
+ randomize: true,
5156
+ onFailedAttempt: /* @__PURE__ */ __name(() => {
5157
+ }, "onFailedAttempt")
5158
+ };
5159
+ function shouldRetry4(error) {
5160
+ if (error instanceof NetworkError4) {
5161
+ return true;
5162
+ }
5163
+ if (error instanceof APIError4) {
5164
+ const status = error.statusCode;
5165
+ if (status >= 500 && status < 600) {
5166
+ return true;
5167
+ }
5168
+ if (status === 429) {
5169
+ return true;
5170
+ }
5171
+ return false;
5172
+ }
5173
+ return true;
5174
+ }
5175
+ __name(shouldRetry4, "shouldRetry");
5176
+ async function withRetry4(fn, config) {
5177
+ const finalConfig = { ...DEFAULT_RETRY_CONFIG4, ...config };
5178
+ return (0, import_p_retry4.default)(
5179
+ async () => {
5180
+ try {
5181
+ return await fn();
5182
+ } catch (error) {
5183
+ if (!shouldRetry4(error)) {
5184
+ throw new import_p_retry4.AbortError(error);
5185
+ }
5186
+ throw error;
5187
+ }
5188
+ },
5189
+ {
5190
+ retries: finalConfig.retries,
5191
+ factor: finalConfig.factor,
5192
+ minTimeout: finalConfig.minTimeout,
5193
+ maxTimeout: finalConfig.maxTimeout,
5194
+ randomize: finalConfig.randomize,
5195
+ onFailedAttempt: finalConfig.onFailedAttempt ? (error) => {
5196
+ const pRetryError = error;
5197
+ finalConfig.onFailedAttempt({
5198
+ error: pRetryError,
5199
+ attemptNumber: pRetryError.attemptNumber,
5200
+ retriesLeft: pRetryError.retriesLeft
5201
+ });
5202
+ } : void 0
5203
+ }
5204
+ );
5205
+ }
5206
+ __name(withRetry4, "withRetry");
5207
+
5208
+ // src/generated/cfg_totp/client.ts
5209
+ var APIClient4 = class {
5210
+ constructor(baseUrl, options) {
5211
+ this.logger = null;
5212
+ this.retryConfig = null;
5213
+ this.baseUrl = baseUrl.replace(/\/$/, "");
5214
+ this.httpClient = options?.httpClient || new FetchAdapter4();
5215
+ if (options?.loggerConfig !== void 0) {
5216
+ this.logger = new APILogger4(options.loggerConfig);
5217
+ }
5218
+ if (options?.retryConfig !== void 0) {
5219
+ this.retryConfig = options.retryConfig;
5220
+ }
5221
+ this.backup_codes = new BackupCodes(this);
5222
+ this.totp_management = new TotpManagement(this);
5223
+ this.totp_setup = new TotpSetup(this);
5224
+ this.totp_verification = new TotpVerification(this);
5225
+ this.totp = new Totp(this);
5226
+ }
5227
+ static {
5228
+ __name(this, "APIClient");
5229
+ }
5230
+ /**
5231
+ * Get CSRF token from cookies (for SessionAuthentication).
5232
+ *
5233
+ * Returns null if cookie doesn't exist (JWT-only auth).
5234
+ */
5235
+ getCsrfToken() {
5236
+ const name = "csrftoken";
5237
+ const value = `; ${document.cookie}`;
5238
+ const parts = value.split(`; ${name}=`);
5239
+ if (parts.length === 2) {
5240
+ return parts.pop()?.split(";").shift() || null;
5241
+ }
5242
+ return null;
5243
+ }
5244
+ /**
5245
+ * Make HTTP request with Django CSRF and session handling.
5246
+ * Automatically retries on network errors and 5xx server errors.
5247
+ */
5248
+ async request(method, path, options) {
5249
+ if (this.retryConfig) {
5250
+ return withRetry4(() => this._makeRequest(method, path, options), {
5251
+ ...this.retryConfig,
5252
+ onFailedAttempt: /* @__PURE__ */ __name((info) => {
5253
+ if (this.logger) {
5254
+ this.logger.warn(
5255
+ `Retry attempt ${info.attemptNumber}/${info.retriesLeft + info.attemptNumber} for ${method} ${path}: ${info.error.message}`
5256
+ );
5257
+ }
5258
+ this.retryConfig?.onFailedAttempt?.(info);
5259
+ }, "onFailedAttempt")
5260
+ });
5261
+ }
5262
+ return this._makeRequest(method, path, options);
5263
+ }
5264
+ /**
5265
+ * Internal request method (without retry wrapper).
5266
+ * Used by request() method with optional retry logic.
5267
+ */
5268
+ async _makeRequest(method, path, options) {
5269
+ const url = this.baseUrl ? `${this.baseUrl}${path}` : path;
5270
+ const startTime = Date.now();
5271
+ const headers = {
5272
+ ...options?.headers || {}
5273
+ };
5274
+ if (!options?.formData && !options?.binaryBody && !headers["Content-Type"]) {
5275
+ headers["Content-Type"] = "application/json";
5276
+ }
5277
+ if (this.logger) {
5278
+ this.logger.logRequest({
5279
+ method,
5280
+ url,
5281
+ headers,
5282
+ body: options?.formData || options?.body,
5283
+ timestamp: startTime
5284
+ });
5285
+ }
5286
+ try {
5287
+ const response = await this.httpClient.request({
5288
+ method,
5289
+ url,
5290
+ headers,
5291
+ params: options?.params,
5292
+ body: options?.body,
5293
+ formData: options?.formData,
5294
+ binaryBody: options?.binaryBody
5295
+ });
5296
+ const duration = Date.now() - startTime;
5297
+ if (response.status >= 400) {
5298
+ const error = new APIError4(
5299
+ response.status,
5300
+ response.statusText,
5301
+ response.data,
5302
+ url
5303
+ );
5304
+ if (this.logger) {
5305
+ this.logger.logError(
5306
+ {
5307
+ method,
5308
+ url,
5309
+ headers,
5310
+ body: options?.formData || options?.body,
5311
+ timestamp: startTime
5312
+ },
5313
+ {
5314
+ message: error.message,
5315
+ statusCode: response.status,
5316
+ duration,
5317
+ timestamp: Date.now()
5318
+ }
5319
+ );
5320
+ }
5321
+ throw error;
5322
+ }
5323
+ if (this.logger) {
5324
+ this.logger.logResponse(
5325
+ {
5326
+ method,
5327
+ url,
5328
+ headers,
5329
+ body: options?.formData || options?.body,
5330
+ timestamp: startTime
5331
+ },
5332
+ {
5333
+ status: response.status,
5334
+ statusText: response.statusText,
5335
+ data: response.data,
5336
+ duration,
5337
+ timestamp: Date.now()
5338
+ }
5339
+ );
5340
+ }
5341
+ return response.data;
5342
+ } catch (error) {
5343
+ const duration = Date.now() - startTime;
5344
+ if (error instanceof APIError4) {
5345
+ throw error;
5346
+ }
5347
+ const isCORSError = error instanceof TypeError && (error.message.toLowerCase().includes("cors") || error.message.toLowerCase().includes("failed to fetch") || error.message.toLowerCase().includes("network request failed"));
5348
+ if (this.logger) {
5349
+ if (isCORSError) {
5350
+ this.logger.error(`\u{1F6AB} CORS Error: ${method} ${url}`);
5351
+ this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
5352
+ this.logger.error(` \u2192 Configure security_domains parameter on the server`);
5353
+ } else {
5354
+ this.logger.error(`\u26A0\uFE0F Network Error: ${method} ${url}`);
5355
+ this.logger.error(` \u2192 ${error instanceof Error ? error.message : String(error)}`);
5356
+ }
5357
+ }
5358
+ if (typeof window !== "undefined") {
5359
+ try {
5360
+ if (isCORSError) {
5361
+ window.dispatchEvent(new CustomEvent("cors-error", {
5362
+ detail: {
5363
+ url,
5364
+ method,
5365
+ error: error instanceof Error ? error.message : String(error),
5366
+ timestamp: /* @__PURE__ */ new Date()
5367
+ },
5368
+ bubbles: true,
5369
+ cancelable: false
5370
+ }));
5371
+ } else {
5372
+ window.dispatchEvent(new CustomEvent("network-error", {
5373
+ detail: {
5374
+ url,
5375
+ method,
5376
+ error: error instanceof Error ? error.message : String(error),
5377
+ timestamp: /* @__PURE__ */ new Date()
5378
+ },
5379
+ bubbles: true,
5380
+ cancelable: false
5381
+ }));
5382
+ }
5383
+ } catch (eventError) {
5384
+ }
5385
+ }
5386
+ const networkError = error instanceof Error ? new NetworkError4(error.message, url, error) : new NetworkError4("Unknown error", url);
5387
+ if (this.logger) {
5388
+ this.logger.logError(
5389
+ {
5390
+ method,
5391
+ url,
5392
+ headers,
5393
+ body: options?.formData || options?.body,
5394
+ timestamp: startTime
5395
+ },
5396
+ {
5397
+ message: networkError.message,
5398
+ duration,
5399
+ timestamp: Date.now()
5400
+ }
5401
+ );
5402
+ }
5403
+ throw networkError;
5404
+ }
5405
+ }
5406
+ };
5407
+
5408
+ // src/generated/cfg_totp/storage.ts
5409
+ var LocalStorageAdapter4 = class {
5410
+ static {
5411
+ __name(this, "LocalStorageAdapter");
5412
+ }
5413
+ constructor(logger2) {
5414
+ this.logger = logger2;
5415
+ }
5416
+ getItem(key) {
5417
+ try {
5418
+ if (typeof window !== "undefined" && window.localStorage) {
5419
+ const value = localStorage.getItem(key);
5420
+ this.logger?.debug(`LocalStorage.getItem("${key}"): ${value ? "found" : "not found"}`);
5421
+ return value;
5422
+ }
5423
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5424
+ } catch (error) {
5425
+ this.logger?.error("LocalStorage.getItem failed:", error);
5426
+ }
5427
+ return null;
5428
+ }
5429
+ setItem(key, value) {
5430
+ try {
5431
+ if (typeof window !== "undefined" && window.localStorage) {
5432
+ localStorage.setItem(key, value);
5433
+ this.logger?.debug(`LocalStorage.setItem("${key}"): success`);
5434
+ } else {
5435
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5436
+ }
5437
+ } catch (error) {
5438
+ this.logger?.error("LocalStorage.setItem failed:", error);
5439
+ }
5440
+ }
5441
+ removeItem(key) {
5442
+ try {
5443
+ if (typeof window !== "undefined" && window.localStorage) {
5444
+ localStorage.removeItem(key);
5445
+ this.logger?.debug(`LocalStorage.removeItem("${key}"): success`);
5446
+ } else {
5447
+ this.logger?.warn("LocalStorage not available: window.localStorage is undefined");
5448
+ }
5449
+ } catch (error) {
5450
+ this.logger?.error("LocalStorage.removeItem failed:", error);
5451
+ }
5452
+ }
5453
+ };
5454
+
5455
+ // src/generated/cfg_totp/enums.ts
5456
+ var DeviceListStatus = /* @__PURE__ */ ((DeviceListStatus2) => {
5457
+ DeviceListStatus2["PENDING"] = "pending";
5458
+ DeviceListStatus2["ACTIVE"] = "active";
5459
+ DeviceListStatus2["DISABLED"] = "disabled";
5460
+ return DeviceListStatus2;
5461
+ })(DeviceListStatus || {});
5462
+
5463
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateRequest.schema.ts
5464
+ var import_zod60 = require("zod");
5465
+ var BackupCodesRegenerateRequestSchema = import_zod60.z.object({
5466
+ code: import_zod60.z.string().min(6).max(6)
5467
+ });
5468
+
5469
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesRegenerateResponse.schema.ts
5470
+ var import_zod61 = require("zod");
5471
+ var BackupCodesRegenerateResponseSchema = import_zod61.z.object({
5472
+ backup_codes: import_zod61.z.array(import_zod61.z.string()),
5473
+ warning: import_zod61.z.string()
5474
+ });
5475
+
5476
+ // src/generated/cfg_totp/_utils/schemas/BackupCodesStatus.schema.ts
5477
+ var import_zod62 = require("zod");
5478
+ var BackupCodesStatusSchema = import_zod62.z.object({
5479
+ remaining_count: import_zod62.z.int(),
5480
+ total_generated: import_zod62.z.int(),
5481
+ warning: import_zod62.z.string().nullable().optional()
5482
+ });
5483
+
5484
+ // src/generated/cfg_totp/_utils/schemas/ConfirmSetupRequest.schema.ts
5485
+ var import_zod63 = require("zod");
5486
+ var ConfirmSetupRequestSchema = import_zod63.z.object({
5487
+ device_id: import_zod63.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5488
+ code: import_zod63.z.string().min(6).max(6)
5489
+ });
5490
+
5491
+ // src/generated/cfg_totp/_utils/schemas/ConfirmSetupResponse.schema.ts
5492
+ var import_zod64 = require("zod");
5493
+ var ConfirmSetupResponseSchema = import_zod64.z.object({
5494
+ message: import_zod64.z.string(),
5495
+ backup_codes: import_zod64.z.array(import_zod64.z.string()),
5496
+ backup_codes_warning: import_zod64.z.string()
5497
+ });
5498
+
5499
+ // src/generated/cfg_totp/_utils/schemas/DeviceList.schema.ts
5500
+ var import_zod65 = require("zod");
5501
+ var DeviceListSchema = import_zod65.z.object({
5502
+ id: import_zod65.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5503
+ name: import_zod65.z.string(),
5504
+ is_primary: import_zod65.z.boolean(),
5505
+ status: import_zod65.z.nativeEnum(DeviceListStatus),
5506
+ created_at: import_zod65.z.iso.datetime(),
5507
+ confirmed_at: import_zod65.z.iso.datetime().nullable(),
5508
+ last_used_at: import_zod65.z.iso.datetime().nullable()
5509
+ });
5510
+
5511
+ // src/generated/cfg_totp/_utils/schemas/DisableRequest.schema.ts
5512
+ var import_zod66 = require("zod");
5513
+ var DisableRequestSchema = import_zod66.z.object({
5514
+ code: import_zod66.z.string().min(6).max(6)
5515
+ });
5516
+
5517
+ // src/generated/cfg_totp/_utils/schemas/PaginatedDeviceListList.schema.ts
5518
+ var import_zod67 = require("zod");
5519
+ var PaginatedDeviceListListSchema = import_zod67.z.object({
5520
+ count: import_zod67.z.int(),
5521
+ page: import_zod67.z.int(),
5522
+ pages: import_zod67.z.int(),
5523
+ page_size: import_zod67.z.int(),
5524
+ has_next: import_zod67.z.boolean(),
5525
+ has_previous: import_zod67.z.boolean(),
5526
+ next_page: import_zod67.z.int().nullable().optional(),
5527
+ previous_page: import_zod67.z.int().nullable().optional(),
5528
+ results: import_zod67.z.array(DeviceListSchema)
5529
+ });
5530
+
5531
+ // src/generated/cfg_totp/_utils/schemas/SetupRequest.schema.ts
5532
+ var import_zod68 = require("zod");
5533
+ var SetupRequestSchema = import_zod68.z.object({
5534
+ device_name: import_zod68.z.string().min(1).max(100).optional()
5535
+ });
5536
+
5537
+ // src/generated/cfg_totp/_utils/schemas/SetupResponse.schema.ts
5538
+ var import_zod69 = require("zod");
5539
+ var SetupResponseSchema = import_zod69.z.object({
5540
+ device_id: import_zod69.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5541
+ secret: import_zod69.z.string(),
5542
+ provisioning_uri: import_zod69.z.string(),
5543
+ qr_code_base64: import_zod69.z.string(),
5544
+ expires_in: import_zod69.z.int()
5545
+ });
5546
+
5547
+ // src/generated/cfg_totp/_utils/schemas/VerifyBackupRequest.schema.ts
5548
+ var import_zod70 = require("zod");
5549
+ var VerifyBackupRequestSchema = import_zod70.z.object({
5550
+ session_id: import_zod70.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5551
+ backup_code: import_zod70.z.string().min(8).max(8)
5552
+ });
5553
+
5554
+ // src/generated/cfg_totp/_utils/schemas/VerifyRequest.schema.ts
5555
+ var import_zod71 = require("zod");
5556
+ var VerifyRequestSchema = import_zod71.z.object({
5557
+ session_id: import_zod71.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
5558
+ code: import_zod71.z.string().min(6).max(6)
5559
+ });
5560
+
5561
+ // src/generated/cfg_totp/_utils/schemas/VerifyResponse.schema.ts
5562
+ var import_zod72 = require("zod");
5563
+ var VerifyResponseSchema = import_zod72.z.object({
5564
+ message: import_zod72.z.string(),
5565
+ access_token: import_zod72.z.string(),
5566
+ refresh_token: import_zod72.z.string(),
5567
+ user: import_zod72.z.record(import_zod72.z.string(), import_zod72.z.any()),
5568
+ remaining_backup_codes: import_zod72.z.int().optional(),
5569
+ warning: import_zod72.z.string().optional()
5570
+ });
5571
+
5572
+ // src/generated/cfg_totp/_utils/fetchers/totp__backup_codes.ts
5573
+ var import_consola15 = require("consola");
5574
+
5575
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_management.ts
5576
+ var import_consola16 = require("consola");
5577
+
5578
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_setup.ts
5579
+ var import_consola17 = require("consola");
5580
+
5581
+ // src/generated/cfg_totp/_utils/fetchers/totp__totp_verification.ts
5582
+ var import_consola18 = require("consola");
5583
+
5584
+ // src/generated/cfg_totp/index.ts
5585
+ var TOKEN_KEY4 = "auth_token";
5586
+ var REFRESH_TOKEN_KEY4 = "refresh_token";
5587
+ var API4 = class {
5588
+ constructor(baseUrl, options) {
5589
+ this._token = null;
5590
+ this._refreshToken = null;
5591
+ this.baseUrl = baseUrl;
5592
+ this.options = options;
5593
+ const logger2 = options?.loggerConfig ? new APILogger4(options.loggerConfig) : void 0;
5594
+ this.storage = options?.storage || new LocalStorageAdapter4(logger2);
5595
+ this._loadTokensFromStorage();
5596
+ this._client = new APIClient4(this.baseUrl, {
5597
+ retryConfig: this.options?.retryConfig,
5598
+ loggerConfig: this.options?.loggerConfig
5599
+ });
5600
+ this._injectAuthHeader();
5601
+ this.backup_codes = this._client.backup_codes;
5602
+ this.totp_management = this._client.totp_management;
5603
+ this.totp_setup = this._client.totp_setup;
5604
+ this.totp_verification = this._client.totp_verification;
5605
+ this.totp = this._client.totp;
5606
+ }
5607
+ static {
5608
+ __name(this, "API");
5609
+ }
5610
+ _loadTokensFromStorage() {
5611
+ this._token = this.storage.getItem(TOKEN_KEY4);
5612
+ this._refreshToken = this.storage.getItem(REFRESH_TOKEN_KEY4);
5613
+ }
5614
+ _reinitClients() {
5615
+ this._client = new APIClient4(this.baseUrl, {
5616
+ retryConfig: this.options?.retryConfig,
5617
+ loggerConfig: this.options?.loggerConfig
5618
+ });
5619
+ this._injectAuthHeader();
5620
+ this.backup_codes = this._client.backup_codes;
5621
+ this.totp_management = this._client.totp_management;
5622
+ this.totp_setup = this._client.totp_setup;
5623
+ this.totp_verification = this._client.totp_verification;
5624
+ this.totp = this._client.totp;
5625
+ }
5626
+ _injectAuthHeader() {
5627
+ const originalRequest = this._client.request.bind(this._client);
5628
+ this._client.request = async (method, path, options) => {
5629
+ const token = this.getToken();
5630
+ const mergedOptions = {
5631
+ ...options,
5632
+ headers: {
5633
+ ...options?.headers || {},
5634
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
5635
+ }
5636
+ };
5637
+ return originalRequest(method, path, mergedOptions);
5638
+ };
5639
+ }
5640
+ /**
5641
+ * Get current JWT token
5642
+ */
5643
+ getToken() {
5644
+ return this.storage.getItem(TOKEN_KEY4);
5645
+ }
5646
+ /**
5647
+ * Get current refresh token
5648
+ */
5649
+ getRefreshToken() {
5650
+ return this.storage.getItem(REFRESH_TOKEN_KEY4);
5651
+ }
5652
+ /**
5653
+ * Set JWT token and refresh token
5654
+ * @param token - JWT access token
5655
+ * @param refreshToken - JWT refresh token (optional)
5656
+ */
5657
+ setToken(token, refreshToken) {
5658
+ this._token = token;
5659
+ this.storage.setItem(TOKEN_KEY4, token);
5660
+ if (refreshToken) {
5661
+ this._refreshToken = refreshToken;
5662
+ this.storage.setItem(REFRESH_TOKEN_KEY4, refreshToken);
5663
+ }
5664
+ this._reinitClients();
5665
+ }
5666
+ /**
5667
+ * Clear all tokens
5668
+ */
5669
+ clearTokens() {
5670
+ this._token = null;
5671
+ this._refreshToken = null;
5672
+ this.storage.removeItem(TOKEN_KEY4);
5673
+ this.storage.removeItem(REFRESH_TOKEN_KEY4);
5674
+ this._reinitClients();
5675
+ }
5676
+ /**
5677
+ * Check if user is authenticated
5678
+ */
5679
+ isAuthenticated() {
5680
+ return !!this.getToken();
5681
+ }
5682
+ /**
5683
+ * Update base URL and reinitialize clients
5684
+ * @param url - New base URL
5685
+ */
5686
+ setBaseUrl(url) {
5687
+ this.baseUrl = url;
5688
+ this._reinitClients();
5689
+ }
5690
+ /**
5691
+ * Get current base URL
5692
+ */
5693
+ getBaseUrl() {
5694
+ return this.baseUrl;
5695
+ }
5696
+ /**
5697
+ * Get OpenAPI schema path
5698
+ * @returns Path to the OpenAPI schema JSON file
5699
+ *
5700
+ * Note: The OpenAPI schema is available in the schema.json file.
5701
+ * You can load it dynamically using:
5702
+ * ```typescript
5703
+ * const schema = await fetch('./schema.json').then(r => r.json());
5704
+ * // or using fs in Node.js:
5705
+ * // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
5706
+ * ```
5707
+ */
5708
+ getSchemaPath() {
5709
+ return "./schema.json";
5710
+ }
5711
+ };
5712
+
5713
+ // src/generated/cfg_webpush/_utils/hooks/webpush__web_push.ts
5714
+ var import_swr7 = __toESM(require("swr"), 1);
5715
+ var import_swr8 = require("swr");
5716
+
5717
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_admin_api.ts
5718
+ var import_swr9 = require("swr");
5719
+
5720
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_auth.ts
5721
+ var import_swr10 = __toESM(require("swr"), 1);
5722
+
5723
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_monitoring.ts
5724
+ var import_swr11 = __toESM(require("swr"), 1);
5725
+
5726
+ // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_testing.ts
5727
+ var import_swr12 = require("swr");
5728
+
5729
+ // src/generated/cfg_totp/_utils/hooks/totp__backup_codes.ts
5730
+ var import_swr13 = __toESM(require("swr"), 1);
5731
+ var import_swr14 = require("swr");
5732
+
5733
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_management.ts
5734
+ var import_swr15 = __toESM(require("swr"), 1);
5735
+ var import_swr16 = require("swr");
5736
+
5737
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_setup.ts
5738
+ var import_swr17 = require("swr");
5739
+
5740
+ // src/generated/cfg_totp/_utils/hooks/totp__totp_verification.ts
5741
+ var import_swr18 = require("swr");
5742
+
5743
+ // src/generated/cfg_totp/_utils/hooks/totp.ts
5744
+ var import_swr19 = require("swr");
5745
+
5746
+ // src/clients.ts
5747
+ var isStaticBuild3 = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
5748
+ var apiUrl2 = isStaticBuild3 ? "" : process.env.NEXT_PUBLIC_API_URL || "";
5749
+ var storage = new LocalStorageAdapter();
5750
+ var apiAccounts = new API(apiUrl2, { storage });
5751
+ var apiTotp = new API4(apiUrl2, { storage });
5752
+ var apiWebPush = new API3(apiUrl2, { storage });
5753
+ var apiCentrifugo = new API2(apiUrl2, { storage });
5754
+
5755
+ // src/auth/hooks/useTwoFactor.ts
5756
+ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
5757
+ const { onSuccess, onError, redirectUrl, skipRedirect = false } = options;
5758
+ const router = (0, import_hooks4.useCfgRouter)();
5759
+ const [isLoading, setIsLoading] = (0, import_react7.useState)(false);
5760
+ const [error, setError] = (0, import_react7.useState)(null);
5761
+ const [warning, setWarning] = (0, import_react7.useState)(null);
5762
+ const [remainingBackupCodes, setRemainingBackupCodes] = (0, import_react7.useState)(null);
5763
+ const clearError = (0, import_react7.useCallback)(() => {
5764
+ setError(null);
5765
+ }, []);
5766
+ const handleSuccess = (0, import_react7.useCallback)((response) => {
5767
+ apiAccounts.setToken(response.access_token, response.refresh_token);
5768
+ if (response.warning) {
5769
+ setWarning(response.warning);
5770
+ }
5771
+ if (response.remaining_backup_codes !== void 0) {
5772
+ setRemainingBackupCodes(response.remaining_backup_codes);
5773
+ }
5774
+ Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
5775
+ category: "auth" /* AUTH */,
5776
+ label: "2fa"
5777
+ });
5778
+ if (response.user?.id) {
5779
+ Analytics.setUser(String(response.user.id));
5780
+ }
5781
+ onSuccess?.(response.user);
5782
+ if (!skipRedirect) {
5783
+ const finalRedirectUrl = redirectUrl || "/dashboard";
5784
+ authLogger.info("2FA successful, redirecting to:", finalRedirectUrl);
5785
+ router.hardPush(finalRedirectUrl);
5786
+ }
5787
+ }, [onSuccess, redirectUrl, router, skipRedirect]);
5788
+ const verifyTOTP = (0, import_react7.useCallback)(async (sessionId, code) => {
5789
+ if (!sessionId) {
5790
+ const msg = "Missing 2FA session ID";
5791
+ setError(msg);
5792
+ onError?.(msg);
5793
+ return false;
5794
+ }
5795
+ if (!code || code.length !== 6) {
5796
+ const msg = "Please enter a 6-digit code";
5797
+ setError(msg);
5798
+ onError?.(msg);
5799
+ return false;
5800
+ }
5801
+ setIsLoading(true);
5802
+ setError(null);
5803
+ try {
5804
+ authLogger.info("Verifying TOTP code...");
5805
+ const response = await apiTotp.totp_verification.totpVerifyCreate({
5806
+ session_id: sessionId,
5807
+ code
5808
+ });
5809
+ if (!response.access_token || !response.refresh_token) {
5810
+ throw new Error("Invalid response from 2FA verification");
5811
+ }
5812
+ handleSuccess(response);
5813
+ return true;
5814
+ } catch (err) {
5815
+ const errorMessage = err instanceof Error ? err.message : "Invalid verification code";
5816
+ authLogger.error("2FA TOTP verification error:", err);
5817
+ setError(errorMessage);
5818
+ onError?.(errorMessage);
5819
+ Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
5820
+ category: "auth" /* AUTH */,
5821
+ label: "2fa-totp"
5822
+ });
5823
+ return false;
5824
+ } finally {
5825
+ setIsLoading(false);
5826
+ }
5827
+ }, [handleSuccess, onError]);
5828
+ const verifyBackupCode = (0, import_react7.useCallback)(async (sessionId, backupCode) => {
5829
+ if (!sessionId) {
5830
+ const msg = "Missing 2FA session ID";
5831
+ setError(msg);
5832
+ onError?.(msg);
5833
+ return false;
5834
+ }
5835
+ if (!backupCode || backupCode.length < 8) {
5836
+ const msg = "Please enter your backup code";
5837
+ setError(msg);
5838
+ onError?.(msg);
5839
+ return false;
5840
+ }
5841
+ setIsLoading(true);
5842
+ setError(null);
5843
+ try {
5844
+ authLogger.info("Verifying backup code...");
5845
+ const response = await apiTotp.totp_verification.totpVerifyBackupCreate({
5846
+ session_id: sessionId,
5847
+ backup_code: backupCode.replace(/\s+/g, "")
5848
+ // Remove spaces
5849
+ });
5850
+ if (!response.access_token || !response.refresh_token) {
5851
+ throw new Error("Invalid response from backup code verification");
5852
+ }
5853
+ handleSuccess(response);
5854
+ return true;
5855
+ } catch (err) {
5856
+ const errorMessage = err instanceof Error ? err.message : "Invalid backup code";
5857
+ authLogger.error("2FA backup code verification error:", err);
5858
+ setError(errorMessage);
5859
+ onError?.(errorMessage);
5860
+ Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
5861
+ category: "auth" /* AUTH */,
5862
+ label: "2fa-backup"
5863
+ });
5864
+ return false;
5865
+ } finally {
5866
+ setIsLoading(false);
5867
+ }
5868
+ }, [handleSuccess, onError]);
5869
+ return {
5870
+ isLoading,
5871
+ error,
5872
+ warning,
5873
+ remainingBackupCodes,
5874
+ verifyTOTP,
5875
+ verifyBackupCode,
5876
+ clearError
5877
+ };
5878
+ }, "useTwoFactor");
5879
+
5880
+ // src/auth/hooks/useAuthForm.ts
5881
+ var useAuthForm = /* @__PURE__ */ __name((options) => {
5882
+ const {
5883
+ onIdentifierSuccess,
5884
+ onOTPSuccess,
5885
+ onError,
5886
+ sourceUrl,
5887
+ redirectUrl,
5888
+ requireTermsAcceptance = false,
5889
+ authPath = "/auth"
5890
+ } = options;
5891
+ const formState = useAuthFormState();
5892
+ const validation = useAuthValidation();
5893
+ const isAutoSubmitFromUrlRef = (0, import_react8.useRef)(false);
5894
+ const {
5895
+ requestOTP,
5896
+ verifyOTP,
5897
+ getSavedEmail,
5898
+ saveEmail,
5899
+ clearSavedEmail,
5900
+ getSavedPhone,
5901
+ savePhone,
5902
+ clearSavedPhone
5903
+ } = useAuth();
5904
+ const {
5905
+ identifier,
5906
+ channel,
5907
+ otp,
5908
+ isLoading,
5909
+ acceptedTerms,
5910
+ twoFactorSessionId,
5911
+ twoFactorCode,
5912
+ useBackupCode,
5913
+ setIdentifier,
5914
+ setChannel,
5915
+ setOtp,
5916
+ setStep,
5917
+ setError,
5918
+ setIsLoading,
5919
+ clearError,
5920
+ setTwoFactorSessionId,
5921
+ setShouldPrompt2FA,
5922
+ setTwoFactorCode,
5923
+ setUseBackupCode
5924
+ } = formState;
5925
+ const twoFactor = useTwoFactor({
5926
+ onSuccess: /* @__PURE__ */ __name(() => {
5927
+ authLogger.info("2FA verification successful, showing success screen");
5928
+ setStep("success");
5929
+ onOTPSuccess?.();
5930
+ }, "onSuccess"),
5931
+ onError: /* @__PURE__ */ __name((error) => {
5932
+ setError(error);
5933
+ onError?.(error);
5934
+ }, "onError"),
5935
+ redirectUrl,
5936
+ skipRedirect: true
5937
+ // We handle navigation via success step
5938
+ });
5939
+ const { detectChannelFromIdentifier: detectChannelFromIdentifier2, validateIdentifier: validateIdentifier2 } = validation;
5940
+ const saveIdentifierToStorage = (0, import_react8.useCallback)((id, ch) => {
5941
+ if (ch === "email") {
5942
+ saveEmail(id);
5943
+ clearSavedPhone();
5944
+ } else {
5945
+ savePhone(id);
5946
+ clearSavedEmail();
5947
+ }
5948
+ }, [saveEmail, savePhone, clearSavedEmail, clearSavedPhone]);
5949
+ (0, import_react8.useEffect)(() => {
5950
+ const savedPhone = getSavedPhone();
5951
+ const savedEmail = getSavedEmail();
5952
+ if (savedPhone) {
5953
+ setIdentifier(savedPhone);
5954
+ setChannel("phone");
5955
+ } else if (savedEmail) {
5956
+ setIdentifier(savedEmail);
5957
+ setChannel("email");
5958
+ }
5959
+ }, [getSavedEmail, getSavedPhone, setIdentifier, setChannel]);
5960
+ (0, import_react8.useEffect)(() => {
5961
+ if (identifier) {
5962
+ const detected = detectChannelFromIdentifier2(identifier);
5963
+ if (detected && detected !== channel) {
5964
+ setChannel(detected);
5965
+ }
5966
+ }
5967
+ }, [identifier, channel, detectChannelFromIdentifier2, setChannel]);
5968
+ const handleIdentifierSubmit = (0, import_react8.useCallback)(async (e) => {
5969
+ e.preventDefault();
5970
+ if (!identifier) {
5971
+ const msg = channel === "phone" ? "Please enter your phone number" : "Please enter your email address";
5972
+ setError(msg);
5973
+ onError?.(msg);
5974
+ return;
5975
+ }
5976
+ if (!validateIdentifier2(identifier, channel)) {
5977
+ const msg = channel === "phone" ? "Please enter a valid phone number (e.g., +1234567890)" : "Please enter a valid email address";
5978
+ setError(msg);
5979
+ onError?.(msg);
5980
+ return;
4835
5981
  }
4836
5982
  if (requireTermsAcceptance && !acceptedTerms) {
4837
- const message = "Please accept the Terms of Service and Privacy Policy";
4838
- setError(message);
4839
- onError?.(message);
5983
+ const msg = "Please accept the Terms of Service and Privacy Policy";
5984
+ setError(msg);
5985
+ onError?.(msg);
4840
5986
  return;
4841
5987
  }
4842
5988
  setIsLoading(true);
@@ -4844,185 +5990,206 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4844
5990
  try {
4845
5991
  const result = await requestOTP(identifier, channel, sourceUrl);
4846
5992
  if (result.success) {
4847
- if (channel === "email") {
4848
- saveEmail(identifier);
4849
- setSavedPhone("");
4850
- } else if (channel === "phone") {
4851
- savePhone(identifier);
4852
- setSavedEmail("");
4853
- }
4854
- setSavedTermsAccepted(true);
5993
+ saveIdentifierToStorage(identifier, channel);
4855
5994
  setStep("otp");
4856
5995
  onIdentifierSuccess?.(identifier, channel);
4857
5996
  } else {
4858
5997
  setError(result.message);
4859
5998
  onError?.(result.message);
4860
5999
  }
4861
- } catch (error2) {
4862
- const message = "An unexpected error occurred";
4863
- setError(message);
4864
- onError?.(message);
6000
+ } catch {
6001
+ const msg = "An unexpected error occurred";
6002
+ setError(msg);
6003
+ onError?.(msg);
4865
6004
  } finally {
4866
6005
  setIsLoading(false);
4867
6006
  }
4868
- }, [identifier, channel, acceptedTerms, validateIdentifier, requestOTP, saveEmail, clearError, setSavedTermsAccepted, onIdentifierSuccess, onError, sourceUrl]);
4869
- const handleOTPSubmit = (0, import_react7.useCallback)(async (e) => {
4870
- e.preventDefault();
4871
- if (!otp || otp.length < 6) {
4872
- const message = "Please enter the 6-digit verification code";
4873
- setError(message);
4874
- onError?.(message);
4875
- return;
6007
+ }, [
6008
+ identifier,
6009
+ channel,
6010
+ acceptedTerms,
6011
+ requireTermsAcceptance,
6012
+ validateIdentifier2,
6013
+ requestOTP,
6014
+ saveIdentifierToStorage,
6015
+ setError,
6016
+ setIsLoading,
6017
+ setStep,
6018
+ clearError,
6019
+ onIdentifierSuccess,
6020
+ onError,
6021
+ sourceUrl
6022
+ ]);
6023
+ const submitOTP = (0, import_react8.useCallback)(async (submitIdentifier, submitOtp, submitChannel) => {
6024
+ if (!submitOtp || submitOtp.length < 6) {
6025
+ const msg = "Please enter the 6-digit verification code";
6026
+ setError(msg);
6027
+ onError?.(msg);
6028
+ return false;
4876
6029
  }
4877
6030
  setIsLoading(true);
4878
6031
  clearError();
4879
6032
  try {
4880
- const result = await verifyOTP(identifier, otp, channel, sourceUrl, redirectUrl);
6033
+ const result = await verifyOTP(submitIdentifier, submitOtp, submitChannel, sourceUrl, redirectUrl, true);
6034
+ if (result.requires_2fa && result.session_id) {
6035
+ authLogger.info("2FA required after OTP verification");
6036
+ setTwoFactorSessionId(result.session_id);
6037
+ setShouldPrompt2FA(result.should_prompt_2fa || false);
6038
+ setStep("2fa");
6039
+ saveIdentifierToStorage(submitIdentifier, submitChannel);
6040
+ return true;
6041
+ }
4881
6042
  if (result.success) {
4882
- if (channel === "email") {
4883
- setSavedEmail(identifier);
4884
- setSavedPhone("");
4885
- } else if (channel === "phone") {
4886
- setSavedPhone(identifier);
4887
- setSavedEmail("");
6043
+ saveIdentifierToStorage(submitIdentifier, submitChannel);
6044
+ if (result.should_prompt_2fa) {
6045
+ authLogger.info("OTP verification successful, prompting 2FA setup");
6046
+ setShouldPrompt2FA(true);
6047
+ setStep("2fa-setup");
6048
+ onOTPSuccess?.();
6049
+ return true;
4888
6050
  }
6051
+ authLogger.info("OTP verification successful, showing success screen");
6052
+ setStep("success");
4889
6053
  onOTPSuccess?.();
6054
+ return true;
4890
6055
  } else {
4891
6056
  setError(result.message);
4892
6057
  onError?.(result.message);
6058
+ return false;
4893
6059
  }
4894
- } catch (error2) {
4895
- const message = "An unexpected error occurred";
4896
- setError(message);
4897
- onError?.(message);
6060
+ } catch {
6061
+ const msg = "An unexpected error occurred";
6062
+ setError(msg);
6063
+ onError?.(msg);
6064
+ return false;
4898
6065
  } finally {
4899
6066
  setIsLoading(false);
4900
6067
  }
4901
- }, [identifier, otp, channel, verifyOTP, clearError, setSavedEmail, onOTPSuccess, onError, sourceUrl, redirectUrl]);
4902
- const handleResendOTP = (0, import_react7.useCallback)(async () => {
6068
+ }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
6069
+ const handleOTPSubmit = (0, import_react8.useCallback)(async (e) => {
6070
+ e.preventDefault();
6071
+ await submitOTP(identifier, otp, channel);
6072
+ }, [identifier, otp, channel, submitOTP]);
6073
+ const handleResendOTP = (0, import_react8.useCallback)(async () => {
4903
6074
  setIsLoading(true);
4904
6075
  clearError();
4905
6076
  try {
4906
6077
  const result = await requestOTP(identifier, channel, sourceUrl);
4907
6078
  if (result.success) {
4908
- if (channel === "email") {
4909
- saveEmail(identifier);
4910
- setSavedPhone("");
4911
- } else if (channel === "phone") {
4912
- savePhone(identifier);
4913
- setSavedEmail("");
4914
- }
6079
+ saveIdentifierToStorage(identifier, channel);
4915
6080
  setOtp("");
4916
6081
  } else {
4917
6082
  setError(result.message);
4918
6083
  onError?.(result.message);
4919
6084
  }
4920
- } catch (error2) {
4921
- const message = "Failed to resend verification code";
4922
- setError(message);
4923
- onError?.(message);
6085
+ } catch {
6086
+ const msg = "Failed to resend verification code";
6087
+ setError(msg);
6088
+ onError?.(msg);
4924
6089
  } finally {
4925
6090
  setIsLoading(false);
4926
6091
  }
4927
- }, [identifier, channel, requestOTP, saveEmail, clearError, setOtp, onError, sourceUrl]);
4928
- const handleBackToIdentifier = (0, import_react7.useCallback)(() => {
6092
+ }, [identifier, channel, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, onError, sourceUrl]);
6093
+ const handleBackToIdentifier = (0, import_react8.useCallback)(() => {
4929
6094
  setStep("identifier");
4930
6095
  clearError();
4931
- }, [clearError]);
4932
- const forceOTPStep = (0, import_react7.useCallback)(() => {
6096
+ }, [setStep, clearError]);
6097
+ const forceOTPStep = (0, import_react8.useCallback)(() => {
4933
6098
  setStep("otp");
4934
6099
  clearError();
4935
- }, [clearError]);
4936
- const handleAcceptedTermsChange = (0, import_react7.useCallback)((checked) => {
4937
- setAcceptedTerms(checked);
4938
- setSavedTermsAccepted(checked);
4939
- }, [setSavedTermsAccepted]);
6100
+ }, [setStep, clearError]);
6101
+ const handle2FASubmit = (0, import_react8.useCallback)(async (e) => {
6102
+ e.preventDefault();
6103
+ if (!twoFactorSessionId) {
6104
+ const msg = "Missing 2FA session";
6105
+ setError(msg);
6106
+ onError?.(msg);
6107
+ return;
6108
+ }
6109
+ if (useBackupCode) {
6110
+ await twoFactor.verifyBackupCode(twoFactorSessionId, twoFactorCode);
6111
+ } else {
6112
+ await twoFactor.verifyTOTP(twoFactorSessionId, twoFactorCode);
6113
+ }
6114
+ }, [twoFactorSessionId, twoFactorCode, useBackupCode, twoFactor, setError, onError]);
6115
+ const handleUseBackupCode = (0, import_react8.useCallback)(() => {
6116
+ setUseBackupCode(true);
6117
+ setTwoFactorCode("");
6118
+ clearError();
6119
+ }, [setUseBackupCode, setTwoFactorCode, clearError]);
6120
+ const handleUseTOTP = (0, import_react8.useCallback)(() => {
6121
+ setUseBackupCode(false);
6122
+ setTwoFactorCode("");
6123
+ clearError();
6124
+ }, [setUseBackupCode, setTwoFactorCode, clearError]);
4940
6125
  useAutoAuth({
4941
- allowedPaths: [authPath],
4942
- onOTPDetected: /* @__PURE__ */ __name((otp2) => {
4943
- authLogger.info("OTP detected, auto-submitting");
4944
- const savedEmail2 = getSavedEmail();
4945
- const savedPhone2 = getSavedPhone();
4946
- if (savedPhone2) {
4947
- setIdentifier(savedPhone2);
4948
- setChannel("phone");
4949
- } else if (savedEmail2) {
4950
- setIdentifier(savedEmail2);
4951
- setChannel("email");
4952
- }
4953
- setOtp(otp2);
4954
- setStep("otp");
4955
- setTimeout(() => {
4956
- const fakeEvent = { preventDefault: /* @__PURE__ */ __name(() => {
4957
- }, "preventDefault") };
4958
- handleOTPSubmit(fakeEvent);
4959
- }, 200);
4960
- }, "onOTPDetected"),
4961
- cleanupUrl: true
4962
- });
4963
- return {
4964
- // Form state
4965
- identifier,
4966
- channel,
4967
- otp,
4968
- isLoading,
4969
- acceptedTerms,
4970
- step,
4971
- error,
4972
- // Form handlers
4973
- setIdentifier,
4974
- setChannel,
4975
- setOtp,
4976
- setAcceptedTerms: handleAcceptedTermsChange,
4977
- setError,
4978
- clearError,
4979
- // Auth handlers
6126
+ allowedPaths: [authPath],
6127
+ onOTPDetected: /* @__PURE__ */ __name((detectedOtp) => {
6128
+ if (isAutoSubmitFromUrlRef.current || isLoading) return;
6129
+ isAutoSubmitFromUrlRef.current = true;
6130
+ authLogger.info("OTP detected from URL, auto-submitting");
6131
+ const savedPhone = getSavedPhone();
6132
+ const savedEmail = getSavedEmail();
6133
+ let autoIdentifier = "";
6134
+ let autoChannel = "email";
6135
+ if (savedPhone) {
6136
+ autoIdentifier = savedPhone;
6137
+ autoChannel = "phone";
6138
+ } else if (savedEmail) {
6139
+ autoIdentifier = savedEmail;
6140
+ autoChannel = "email";
6141
+ }
6142
+ if (!autoIdentifier) {
6143
+ authLogger.warn("No saved identifier found for auto-submit");
6144
+ isAutoSubmitFromUrlRef.current = false;
6145
+ return;
6146
+ }
6147
+ setIdentifier(autoIdentifier);
6148
+ setChannel(autoChannel);
6149
+ setOtp(detectedOtp);
6150
+ setStep("otp");
6151
+ setTimeout(async () => {
6152
+ try {
6153
+ await submitOTP(autoIdentifier, detectedOtp, autoChannel);
6154
+ } finally {
6155
+ isAutoSubmitFromUrlRef.current = false;
6156
+ }
6157
+ }, 100);
6158
+ }, "onOTPDetected"),
6159
+ cleanupUrl: true
6160
+ });
6161
+ return {
6162
+ // State
6163
+ ...formState,
6164
+ // Submit handlers
4980
6165
  handleIdentifierSubmit,
4981
6166
  handleOTPSubmit,
4982
6167
  handleResendOTP,
4983
6168
  handleBackToIdentifier,
4984
6169
  forceOTPStep,
4985
- // Utility methods
4986
- detectChannelFromIdentifier,
4987
- validateIdentifier
6170
+ // 2FA handlers
6171
+ handle2FASubmit,
6172
+ handleUseBackupCode,
6173
+ handleUseTOTP,
6174
+ // Validation
6175
+ ...validation,
6176
+ // Auto-submit ref
6177
+ isAutoSubmittingFromUrl: isAutoSubmitFromUrlRef,
6178
+ // 2FA state from hook (for loading indicator)
6179
+ is2FALoading: twoFactor.isLoading,
6180
+ twoFactorWarning: twoFactor.warning
4988
6181
  };
4989
6182
  }, "useAuthForm");
4990
6183
 
4991
6184
  // src/auth/hooks/useGithubAuth.ts
4992
- var import_react8 = require("react");
6185
+ var import_react9 = require("react");
4993
6186
  var import_hooks5 = require("@djangocfg/ui-nextjs/hooks");
4994
-
4995
- // src/generated/cfg_webpush/_utils/hooks/webpush__web_push.ts
4996
- var import_swr7 = __toESM(require("swr"), 1);
4997
- var import_swr8 = require("swr");
4998
-
4999
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_admin_api.ts
5000
- var import_swr9 = require("swr");
5001
-
5002
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_auth.ts
5003
- var import_swr10 = __toESM(require("swr"), 1);
5004
-
5005
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_monitoring.ts
5006
- var import_swr11 = __toESM(require("swr"), 1);
5007
-
5008
- // src/generated/cfg_centrifugo/_utils/hooks/centrifugo__centrifugo_testing.ts
5009
- var import_swr12 = require("swr");
5010
-
5011
- // src/clients.ts
5012
- var isStaticBuild3 = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
5013
- var apiUrl2 = isStaticBuild3 ? "" : process.env.NEXT_PUBLIC_API_URL || "";
5014
- var storage = new LocalStorageAdapter();
5015
- var apiAccounts = new API(apiUrl2, { storage });
5016
- var apiWebPush = new API3(apiUrl2, { storage });
5017
- var apiCentrifugo = new API2(apiUrl2, { storage });
5018
-
5019
- // src/auth/hooks/useGithubAuth.ts
5020
6187
  var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5021
- const { sourceUrl, onSuccess, onError, redirectUrl } = options;
6188
+ const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
5022
6189
  const router = (0, import_hooks5.useCfgRouter)();
5023
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
5024
- const [error, setError] = (0, import_react8.useState)(null);
5025
- const startGithubAuth = (0, import_react8.useCallback)(async () => {
6190
+ const [isLoading, setIsLoading] = (0, import_react9.useState)(false);
6191
+ const [error, setError] = (0, import_react9.useState)(null);
6192
+ const startGithubAuth = (0, import_react9.useCallback)(async () => {
5026
6193
  setIsLoading(true);
5027
6194
  setError(null);
5028
6195
  try {
@@ -5056,7 +6223,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5056
6223
  setIsLoading(false);
5057
6224
  }
5058
6225
  }, [sourceUrl, onError]);
5059
- const handleGithubCallback = (0, import_react8.useCallback)(async (code, state) => {
6226
+ const handleGithubCallback = (0, import_react9.useCallback)(async (code, state) => {
5060
6227
  setIsLoading(true);
5061
6228
  setError(null);
5062
6229
  try {
@@ -5073,6 +6240,15 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5073
6240
  code,
5074
6241
  state
5075
6242
  });
6243
+ if (response.requires_2fa && response.session_id) {
6244
+ authLogger.info("GitHub OAuth requires 2FA, session:", response.session_id);
6245
+ Analytics.event("auth_oauth_start" /* AUTH_OAUTH_START */, {
6246
+ category: "auth" /* AUTH */,
6247
+ label: "github-2fa-required"
6248
+ });
6249
+ onRequires2FA?.(response.session_id, response.should_prompt_2fa || false);
6250
+ return;
6251
+ }
5076
6252
  if (!response.access || !response.refresh) {
5077
6253
  throw new Error("Invalid response from OAuth callback");
5078
6254
  }
@@ -5086,8 +6262,10 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5086
6262
  Analytics.setUser(String(response.user.id));
5087
6263
  }
5088
6264
  onSuccess?.(response.user, response.is_new_user || false);
5089
- const finalRedirectUrl = redirectUrl || "/dashboard";
5090
- router.hardPush(finalRedirectUrl);
6265
+ if (!skipRedirect) {
6266
+ const finalRedirectUrl = redirectUrl || "/dashboard";
6267
+ router.hardPush(finalRedirectUrl);
6268
+ }
5091
6269
  } catch (err) {
5092
6270
  const errorMessage = err instanceof Error ? err.message : "GitHub authentication failed";
5093
6271
  authLogger.error("GitHub OAuth callback error:", err);
@@ -5109,6 +6287,291 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
5109
6287
  };
5110
6288
  }, "useGithubAuth");
5111
6289
 
6290
+ // src/auth/hooks/useTwoFactorSetup.ts
6291
+ var import_react10 = require("react");
6292
+ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
6293
+ const { onComplete, onError } = options;
6294
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
6295
+ const [error, setError] = (0, import_react10.useState)(null);
6296
+ const [setupData, setSetupData] = (0, import_react10.useState)(null);
6297
+ const [backupCodes, setBackupCodes] = (0, import_react10.useState)(null);
6298
+ const [backupCodesWarning, setBackupCodesWarning] = (0, import_react10.useState)(null);
6299
+ const [setupStep, setSetupStep] = (0, import_react10.useState)("idle");
6300
+ const clearError = (0, import_react10.useCallback)(() => {
6301
+ setError(null);
6302
+ }, []);
6303
+ const resetSetup = (0, import_react10.useCallback)(() => {
6304
+ setSetupData(null);
6305
+ setBackupCodes(null);
6306
+ setBackupCodesWarning(null);
6307
+ setSetupStep("idle");
6308
+ setError(null);
6309
+ }, []);
6310
+ const startSetup = (0, import_react10.useCallback)(async (deviceName) => {
6311
+ setIsLoading(true);
6312
+ setError(null);
6313
+ setSetupStep("scanning");
6314
+ try {
6315
+ authLogger.info("Starting 2FA setup...");
6316
+ const response = await apiTotp.totp_setup.create({
6317
+ device_name: deviceName
6318
+ });
6319
+ const data = {
6320
+ deviceId: response.device_id,
6321
+ secret: response.secret,
6322
+ provisioningUri: response.provisioning_uri,
6323
+ qrCodeBase64: response.qr_code_base64,
6324
+ expiresIn: response.expires_in
6325
+ };
6326
+ setSetupData(data);
6327
+ authLogger.info("2FA setup initiated, expires in:", data.expiresIn, "seconds");
6328
+ return data;
6329
+ } catch (err) {
6330
+ const errorMessage = err instanceof Error ? err.message : "Failed to start 2FA setup";
6331
+ authLogger.error("2FA setup error:", err);
6332
+ setError(errorMessage);
6333
+ setSetupStep("idle");
6334
+ onError?.(errorMessage);
6335
+ return null;
6336
+ } finally {
6337
+ setIsLoading(false);
6338
+ }
6339
+ }, [onError]);
6340
+ const confirmSetup = (0, import_react10.useCallback)(async (code) => {
6341
+ if (!setupData) {
6342
+ const msg = "Setup not started. Call startSetup() first.";
6343
+ setError(msg);
6344
+ onError?.(msg);
6345
+ return null;
6346
+ }
6347
+ if (!code || code.length !== 6) {
6348
+ const msg = "Please enter a 6-digit code";
6349
+ setError(msg);
6350
+ onError?.(msg);
6351
+ return null;
6352
+ }
6353
+ setIsLoading(true);
6354
+ setError(null);
6355
+ setSetupStep("confirming");
6356
+ try {
6357
+ authLogger.info("Confirming 2FA setup...");
6358
+ const response = await apiTotp.totp_setup.confirmCreate({
6359
+ device_id: setupData.deviceId,
6360
+ code
6361
+ });
6362
+ const codes = response.backup_codes;
6363
+ setBackupCodes(codes);
6364
+ setBackupCodesWarning(response.backup_codes_warning);
6365
+ setSetupStep("complete");
6366
+ authLogger.info("2FA setup confirmed, backup codes generated:", codes.length);
6367
+ onComplete?.(codes);
6368
+ return codes;
6369
+ } catch (err) {
6370
+ const errorMessage = err instanceof Error ? err.message : "Invalid code. Please try again.";
6371
+ authLogger.error("2FA setup confirmation error:", err);
6372
+ setError(errorMessage);
6373
+ setSetupStep("scanning");
6374
+ onError?.(errorMessage);
6375
+ return null;
6376
+ } finally {
6377
+ setIsLoading(false);
6378
+ }
6379
+ }, [setupData, onComplete, onError]);
6380
+ return {
6381
+ isLoading,
6382
+ error,
6383
+ setupData,
6384
+ backupCodes,
6385
+ backupCodesWarning,
6386
+ setupStep,
6387
+ startSetup,
6388
+ confirmSetup,
6389
+ resetSetup,
6390
+ clearError
6391
+ };
6392
+ }, "useTwoFactorSetup");
6393
+
6394
+ // src/auth/hooks/useAuthGuard.ts
6395
+ var import_react11 = require("react");
6396
+ var import_hooks6 = require("@djangocfg/ui-nextjs/hooks");
6397
+ var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
6398
+ const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
6399
+ const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
6400
+ const router = (0, import_hooks6.useCfgRouter)();
6401
+ const [isRedirecting, setIsRedirecting] = (0, import_react11.useState)(false);
6402
+ (0, import_react11.useEffect)(() => {
6403
+ if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
6404
+ if (shouldSaveUrl && typeof window !== "undefined") {
6405
+ const currentUrl = window.location.pathname + window.location.search;
6406
+ saveRedirectUrl(currentUrl);
6407
+ }
6408
+ setIsRedirecting(true);
6409
+ router.push(redirectTo);
6410
+ }
6411
+ }, [isAuthenticated, isLoading, router, redirectTo, requireAuth, isRedirecting, shouldSaveUrl, saveRedirectUrl]);
6412
+ return { isAuthenticated, isLoading, isRedirecting };
6413
+ }, "useAuthGuard");
6414
+
6415
+ // src/auth/hooks/useLocalStorage.ts
6416
+ var import_react12 = require("react");
6417
+ function useLocalStorage2(key, initialValue) {
6418
+ const [storedValue, setStoredValue] = (0, import_react12.useState)(() => {
6419
+ if (typeof window === "undefined") {
6420
+ return initialValue;
6421
+ }
6422
+ try {
6423
+ const item = window.localStorage.getItem(key);
6424
+ if (item === null) {
6425
+ return initialValue;
6426
+ }
6427
+ try {
6428
+ return JSON.parse(item);
6429
+ } catch {
6430
+ return item;
6431
+ }
6432
+ } catch (error) {
6433
+ authLogger.error(`Error reading localStorage key "${key}":`, error);
6434
+ return initialValue;
6435
+ }
6436
+ });
6437
+ const checkDataSize = /* @__PURE__ */ __name((data) => {
6438
+ try {
6439
+ const jsonString = JSON.stringify(data);
6440
+ const sizeInBytes = new Blob([jsonString]).size;
6441
+ const sizeInKB = sizeInBytes / 1024;
6442
+ if (sizeInKB > 1024) {
6443
+ authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
6444
+ return false;
6445
+ }
6446
+ return true;
6447
+ } catch (error) {
6448
+ authLogger.error(`Error checking data size for key "${key}":`, error);
6449
+ return false;
6450
+ }
6451
+ }, "checkDataSize");
6452
+ const clearOldData = /* @__PURE__ */ __name(() => {
6453
+ try {
6454
+ const keys = Object.keys(localStorage).filter((key2) => key2 && typeof key2 === "string");
6455
+ if (keys.length > 50) {
6456
+ const itemsToRemove = Math.ceil(keys.length * 0.2);
6457
+ for (let i = 0; i < itemsToRemove; i++) {
6458
+ try {
6459
+ const key2 = keys[i];
6460
+ if (key2) {
6461
+ localStorage.removeItem(key2);
6462
+ localStorage.removeItem(`${key2}_timestamp`);
6463
+ }
6464
+ } catch {
6465
+ }
6466
+ }
6467
+ }
6468
+ } catch (error) {
6469
+ authLogger.error("Error clearing old localStorage data:", error);
6470
+ }
6471
+ }, "clearOldData");
6472
+ const forceClearAll = /* @__PURE__ */ __name(() => {
6473
+ try {
6474
+ const keys = Object.keys(localStorage);
6475
+ for (const key2 of keys) {
6476
+ try {
6477
+ localStorage.removeItem(key2);
6478
+ } catch {
6479
+ }
6480
+ }
6481
+ } catch (error) {
6482
+ authLogger.error("Error force clearing localStorage:", error);
6483
+ }
6484
+ }, "forceClearAll");
6485
+ const setValue = /* @__PURE__ */ __name((value) => {
6486
+ try {
6487
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
6488
+ if (!checkDataSize(valueToStore)) {
6489
+ authLogger.warn(`Data size too large for key "${key}", removing key`);
6490
+ try {
6491
+ window.localStorage.removeItem(key);
6492
+ window.localStorage.removeItem(`${key}_timestamp`);
6493
+ } catch {
6494
+ }
6495
+ setStoredValue(valueToStore);
6496
+ return;
6497
+ }
6498
+ setStoredValue(valueToStore);
6499
+ if (typeof window !== "undefined") {
6500
+ try {
6501
+ if (typeof valueToStore === "string") {
6502
+ window.localStorage.setItem(key, valueToStore);
6503
+ } else {
6504
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6505
+ }
6506
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6507
+ } catch (storageError) {
6508
+ if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
6509
+ authLogger.warn("localStorage quota exceeded, clearing old data...");
6510
+ clearOldData();
6511
+ try {
6512
+ if (typeof valueToStore === "string") {
6513
+ window.localStorage.setItem(key, valueToStore);
6514
+ } else {
6515
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6516
+ }
6517
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6518
+ } catch (retryError) {
6519
+ authLogger.error(`Failed to set localStorage key "${key}" after clearing old data:`, retryError);
6520
+ try {
6521
+ forceClearAll();
6522
+ if (typeof valueToStore === "string") {
6523
+ window.localStorage.setItem(key, valueToStore);
6524
+ } else {
6525
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
6526
+ }
6527
+ window.localStorage.setItem(`${key}_timestamp`, Date.now().toString());
6528
+ } catch (finalError) {
6529
+ authLogger.error(`Failed to set localStorage key "${key}" after force clearing:`, finalError);
6530
+ setStoredValue(valueToStore);
6531
+ }
6532
+ }
6533
+ } else {
6534
+ throw storageError;
6535
+ }
6536
+ }
6537
+ }
6538
+ } catch (error) {
6539
+ authLogger.error(`Error setting localStorage key "${key}":`, error);
6540
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
6541
+ setStoredValue(valueToStore);
6542
+ }
6543
+ }, "setValue");
6544
+ const removeValue = /* @__PURE__ */ __name(() => {
6545
+ try {
6546
+ setStoredValue(initialValue);
6547
+ if (typeof window !== "undefined") {
6548
+ try {
6549
+ window.localStorage.removeItem(key);
6550
+ window.localStorage.removeItem(`${key}_timestamp`);
6551
+ } catch (removeError) {
6552
+ if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
6553
+ authLogger.warn("localStorage quota exceeded during removal, clearing old data...");
6554
+ clearOldData();
6555
+ try {
6556
+ window.localStorage.removeItem(key);
6557
+ window.localStorage.removeItem(`${key}_timestamp`);
6558
+ } catch (retryError) {
6559
+ authLogger.error(`Failed to remove localStorage key "${key}" after clearing:`, retryError);
6560
+ forceClearAll();
6561
+ }
6562
+ } else {
6563
+ throw removeError;
6564
+ }
6565
+ }
6566
+ }
6567
+ } catch (error) {
6568
+ authLogger.error(`Error removing localStorage key "${key}":`, error);
6569
+ }
6570
+ }, "removeValue");
6571
+ return [storedValue, setValue, removeValue];
6572
+ }
6573
+ __name(useLocalStorage2, "useLocalStorage");
6574
+
5112
6575
  // src/auth/utils/validation.ts
5113
6576
  var validateEmail = /* @__PURE__ */ __name((email) => {
5114
6577
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;