@djangocfg/api 2.1.227 → 2.1.229

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 (55) hide show
  1. package/README.md +8 -9
  2. package/dist/auth-server.cjs +4 -9
  3. package/dist/auth-server.cjs.map +1 -1
  4. package/dist/auth-server.mjs +4 -9
  5. package/dist/auth-server.mjs.map +1 -1
  6. package/dist/auth.cjs +120 -158
  7. package/dist/auth.cjs.map +1 -1
  8. package/dist/auth.d.cts +120 -177
  9. package/dist/auth.d.ts +120 -177
  10. package/dist/auth.mjs +149 -191
  11. package/dist/auth.mjs.map +1 -1
  12. package/dist/clients.cjs +5 -11
  13. package/dist/clients.cjs.map +1 -1
  14. package/dist/clients.d.cts +253 -254
  15. package/dist/clients.d.ts +253 -254
  16. package/dist/clients.mjs +5 -11
  17. package/dist/clients.mjs.map +1 -1
  18. package/dist/hooks.cjs +4 -9
  19. package/dist/hooks.cjs.map +1 -1
  20. package/dist/hooks.d.cts +64 -85
  21. package/dist/hooks.d.ts +64 -85
  22. package/dist/hooks.mjs +4 -9
  23. package/dist/hooks.mjs.map +1 -1
  24. package/dist/index.cjs +5 -11
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +109 -99
  27. package/dist/index.d.ts +109 -99
  28. package/dist/index.mjs +5 -11
  29. package/dist/index.mjs.map +1 -1
  30. package/package.json +2 -2
  31. package/src/_api/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts +24 -2
  32. package/src/_api/generated/cfg_accounts/_utils/schemas/OTPRequestRequest.schema.ts +0 -2
  33. package/src/_api/generated/cfg_accounts/_utils/schemas/OTPVerifyRequest.schema.ts +0 -2
  34. package/src/_api/generated/cfg_accounts/accounts/client.ts +1 -1
  35. package/src/_api/generated/cfg_accounts/accounts/models.ts +37 -38
  36. package/src/_api/generated/cfg_accounts/accounts__oauth/models.ts +36 -36
  37. package/src/_api/generated/cfg_accounts/accounts__user_profile/models.ts +15 -15
  38. package/src/_api/generated/cfg_accounts/enums.ts +0 -10
  39. package/src/_api/generated/cfg_accounts/schema.json +31 -25
  40. package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_admin_api/models.ts +74 -74
  41. package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_monitoring/models.ts +36 -36
  42. package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_testing/models.ts +22 -22
  43. package/src/_api/generated/cfg_totp/totp__totp_management/models.ts +10 -10
  44. package/src/_api/generated/cfg_totp/totp__totp_setup/models.ts +22 -22
  45. package/src/_api/generated/cfg_totp/totp__totp_verification/models.ts +8 -8
  46. package/src/auth/context/AccountsContext.tsx +6 -2
  47. package/src/auth/context/AuthContext.tsx +32 -39
  48. package/src/auth/context/types.ts +5 -9
  49. package/src/auth/hooks/index.ts +1 -1
  50. package/src/auth/hooks/useAuthForm.ts +42 -75
  51. package/src/auth/hooks/useAuthFormState.ts +35 -6
  52. package/src/auth/hooks/useAuthValidation.ts +5 -65
  53. package/src/auth/hooks/useTwoFactor.ts +17 -2
  54. package/src/auth/types/form.ts +25 -70
  55. package/src/auth/types/index.ts +2 -6
package/dist/auth.mjs CHANGED
@@ -1,10 +1,6 @@
1
1
  "use client";
2
2
  var __defProp = Object.defineProperty;
3
3
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
- var __export = (target, all) => {
5
- for (var name in all)
6
- __defProp(target, name, { get: all[name], enumerable: true });
7
- };
8
4
 
9
5
  // src/auth/context/AuthContext.tsx
10
6
  import { usePathname as usePathname3 } from "next/navigation";
@@ -12,9 +8,9 @@ import {
12
8
  createContext as createContext2,
13
9
  useCallback as useCallback12,
14
10
  useContext as useContext2,
15
- useEffect as useEffect8,
11
+ useEffect as useEffect9,
16
12
  useMemo as useMemo2,
17
- useRef as useRef5,
13
+ useRef as useRef6,
18
14
  useState as useState12
19
15
  } from "react";
20
16
  import { SWRConfig } from "swr";
@@ -123,10 +119,14 @@ function useQueryParams() {
123
119
  __name(useQueryParams, "useQueryParams");
124
120
 
125
121
  // src/auth/hooks/useAuthFormState.ts
126
- import { useCallback as useCallback2, useState as useState2 } from "react";
127
- var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialChannel = "email") => {
122
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
123
+ var formatCountdown = /* @__PURE__ */ __name((s) => {
124
+ if (s <= 0) return "";
125
+ const m = Math.floor(s / 60);
126
+ return m > 0 ? `${m}:${String(s % 60).padStart(2, "0")}` : `${s}s`;
127
+ }, "formatCountdown");
128
+ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
128
129
  const [identifier, setIdentifier] = useState2(initialIdentifier);
129
- const [channel, setChannel] = useState2(initialChannel);
130
130
  const [otp, setOtp] = useState2("");
131
131
  const [isLoading, setIsLoading] = useState2(false);
132
132
  const [acceptedTerms, setAcceptedTerms] = useState2(true);
@@ -136,11 +136,29 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
136
136
  const [shouldPrompt2FA, setShouldPrompt2FA] = useState2(false);
137
137
  const [twoFactorCode, setTwoFactorCode] = useState2("");
138
138
  const [useBackupCode, setUseBackupCode] = useState2(false);
139
+ const [rateLimitSeconds, setRateLimitSeconds] = useState2(0);
140
+ const rateLimitTimerRef = useRef2(null);
141
+ const startRateLimitCountdown = useCallback2((seconds) => {
142
+ if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
143
+ setRateLimitSeconds(seconds);
144
+ rateLimitTimerRef.current = setInterval(() => {
145
+ setRateLimitSeconds((prev) => {
146
+ if (prev <= 1) {
147
+ clearInterval(rateLimitTimerRef.current);
148
+ rateLimitTimerRef.current = null;
149
+ return 0;
150
+ }
151
+ return prev - 1;
152
+ });
153
+ }, 1e3);
154
+ }, []);
155
+ useEffect2(() => () => {
156
+ if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
157
+ }, []);
139
158
  const clearError = useCallback2(() => setError(""), []);
140
159
  return {
141
160
  // State
142
161
  identifier,
143
- channel,
144
162
  otp,
145
163
  isLoading,
146
164
  acceptedTerms,
@@ -150,9 +168,11 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
150
168
  shouldPrompt2FA,
151
169
  twoFactorCode,
152
170
  useBackupCode,
171
+ rateLimitSeconds,
172
+ isRateLimited: rateLimitSeconds > 0,
173
+ rateLimitLabel: formatCountdown(rateLimitSeconds),
153
174
  // Handlers
154
175
  setIdentifier,
155
- setChannel,
156
176
  setOtp,
157
177
  setAcceptedTerms,
158
178
  setError,
@@ -162,57 +182,24 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
162
182
  setTwoFactorSessionId,
163
183
  setShouldPrompt2FA,
164
184
  setTwoFactorCode,
165
- setUseBackupCode
185
+ setUseBackupCode,
186
+ startRateLimitCountdown
166
187
  };
167
188
  }, "useAuthFormState");
168
189
 
169
190
  // src/auth/hooks/useAuthValidation.ts
170
191
  import { useCallback as useCallback3 } from "react";
171
192
  var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
172
- var PHONE_REGEX = /^\+[1-9]\d{6,14}$/;
173
193
  var useAuthValidation = /* @__PURE__ */ __name(() => {
174
- const detectChannelFromIdentifier2 = useCallback3((id) => {
175
- if (!id) return null;
176
- if (id.includes("@")) {
177
- return "email";
178
- }
179
- if (id.startsWith("+") && PHONE_REGEX.test(id)) {
180
- return "phone";
181
- }
182
- return null;
194
+ const validateIdentifier2 = useCallback3((id) => {
195
+ return EMAIL_REGEX.test(id);
183
196
  }, []);
184
- const validateIdentifier2 = useCallback3((id, channelType) => {
185
- if (!id) return false;
186
- const channel = channelType || detectChannelFromIdentifier2(id);
187
- if (channel === "email") {
188
- return EMAIL_REGEX.test(id);
189
- }
190
- if (channel === "phone") {
191
- return PHONE_REGEX.test(id);
192
- }
193
- return false;
194
- }, [detectChannelFromIdentifier2]);
195
- return {
196
- detectChannelFromIdentifier: detectChannelFromIdentifier2,
197
- validateIdentifier: validateIdentifier2
198
- };
197
+ return { validateIdentifier: validateIdentifier2 };
199
198
  }, "useAuthValidation");
200
- var detectChannelFromIdentifier = /* @__PURE__ */ __name((id) => {
201
- if (!id) return null;
202
- if (id.includes("@")) return "email";
203
- if (id.startsWith("+") && PHONE_REGEX.test(id)) return "phone";
204
- return null;
205
- }, "detectChannelFromIdentifier");
206
- var validateIdentifier = /* @__PURE__ */ __name((id, channelType) => {
207
- if (!id) return false;
208
- const channel = channelType || detectChannelFromIdentifier(id);
209
- if (channel === "email") return EMAIL_REGEX.test(id);
210
- if (channel === "phone") return PHONE_REGEX.test(id);
211
- return false;
212
- }, "validateIdentifier");
199
+ var validateIdentifier = /* @__PURE__ */ __name((id) => EMAIL_REGEX.test(id), "validateIdentifier");
213
200
 
214
201
  // src/auth/hooks/useAuthForm.ts
215
- import { useCallback as useCallback5, useEffect as useEffect3, useRef as useRef2 } from "react";
202
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef3 } from "react";
216
203
 
217
204
  // src/auth/utils/logger.ts
218
205
  import { createConsola } from "consola";
@@ -227,7 +214,7 @@ var authLogger = logger.withTag("auth");
227
214
 
228
215
  // src/auth/hooks/useAutoAuth.ts
229
216
  import { usePathname as usePathname2 } from "next/navigation";
230
- import { useEffect as useEffect2 } from "react";
217
+ import { useEffect as useEffect3 } from "react";
231
218
  var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
232
219
  const { onOTPDetected, cleanupUrl = true, allowedPaths = ["/auth"] } = options;
233
220
  const queryParams = useQueryParams();
@@ -236,7 +223,7 @@ var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
236
223
  const isAllowedPath = allowedPaths.some((path) => pathname === path || pathname?.startsWith(path + "/"));
237
224
  const hasOTP = !!queryParams.get("otp");
238
225
  const isReady = !!pathname && hasOTP && isAllowedPath;
239
- useEffect2(() => {
226
+ useEffect3(() => {
240
227
  if (!isReady) return;
241
228
  const queryOtp = queryParams.get("otp");
242
229
  if (queryOtp && typeof queryOtp === "string" && queryOtp.length === 6) {
@@ -425,7 +412,7 @@ var Accounts = class {
425
412
  this.client = client;
426
413
  }
427
414
  /**
428
- * Request OTP code to email or phone.
415
+ * Request OTP code to email.
429
416
  */
430
417
  async otpRequestCreate(data) {
431
418
  const response = await this.client.request("POST", "/cfg/accounts/otp/request/", { body: data });
@@ -1066,20 +1053,10 @@ var LocalStorageAdapter = class {
1066
1053
  };
1067
1054
 
1068
1055
  // src/_api/generated/cfg_accounts/enums.ts
1069
- var enums_exports = {};
1070
- __export(enums_exports, {
1071
- OAuthConnectionProvider: () => OAuthConnectionProvider,
1072
- OTPRequestRequestChannel: () => OTPRequestRequestChannel
1073
- });
1074
1056
  var OAuthConnectionProvider = /* @__PURE__ */ ((OAuthConnectionProvider2) => {
1075
1057
  OAuthConnectionProvider2["GITHUB"] = "github";
1076
1058
  return OAuthConnectionProvider2;
1077
1059
  })(OAuthConnectionProvider || {});
1078
- var OTPRequestRequestChannel = /* @__PURE__ */ ((OTPRequestRequestChannel2) => {
1079
- OTPRequestRequestChannel2["EMAIL"] = "email";
1080
- OTPRequestRequestChannel2["PHONE"] = "phone";
1081
- return OTPRequestRequestChannel2;
1082
- })(OTPRequestRequestChannel || {});
1083
1060
 
1084
1061
  // src/_api/generated/cfg_accounts/_utils/schemas/AccountDeleteResponse.schema.ts
1085
1062
  import { z } from "zod";
@@ -1173,14 +1150,15 @@ var OAuthTokenResponseSchema = z11.object({
1173
1150
  // src/_api/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts
1174
1151
  import { z as z12 } from "zod";
1175
1152
  var OTPErrorResponseSchema = z12.object({
1176
- error: z12.string()
1153
+ error: z12.string(),
1154
+ error_code: z12.string().nullable().optional(),
1155
+ retry_after: z12.number().int().nullable().optional()
1177
1156
  });
1178
1157
 
1179
1158
  // src/_api/generated/cfg_accounts/_utils/schemas/OTPRequestRequest.schema.ts
1180
1159
  import { z as z13 } from "zod";
1181
1160
  var OTPRequestRequestSchema = z13.object({
1182
1161
  identifier: z13.string().min(1),
1183
- channel: z13.nativeEnum(OTPRequestRequestChannel).optional(),
1184
1162
  source_url: z13.string().optional()
1185
1163
  });
1186
1164
 
@@ -1195,7 +1173,6 @@ import { z as z15 } from "zod";
1195
1173
  var OTPVerifyRequestSchema = z15.object({
1196
1174
  identifier: z15.string().min(1),
1197
1175
  otp: z15.string().min(6).max(6),
1198
- channel: z15.nativeEnum(OTPRequestRequestChannel).optional(),
1199
1176
  source_url: z15.string().optional()
1200
1177
  });
1201
1178
 
@@ -4315,6 +4292,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
4315
4292
  const [error, setError] = useState3(null);
4316
4293
  const [warning, setWarning] = useState3(null);
4317
4294
  const [remainingBackupCodes, setRemainingBackupCodes] = useState3(null);
4295
+ const [attemptsRemaining, setAttemptsRemaining] = useState3(null);
4318
4296
  const clearError = useCallback4(() => {
4319
4297
  setError(null);
4320
4298
  }, []);
@@ -4367,8 +4345,11 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
4367
4345
  handleSuccess(response);
4368
4346
  return true;
4369
4347
  } catch (err) {
4370
- const errorMessage = err instanceof Error ? err.message : "Invalid verification code";
4371
4348
  authLogger.error("2FA TOTP verification error:", err);
4349
+ const errorMessage = err instanceof APIError3 ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid verification code";
4350
+ if (err instanceof APIError3 && typeof err.response?.attempts_remaining === "number") {
4351
+ setAttemptsRemaining(err.response.attempts_remaining);
4352
+ }
4372
4353
  setError(errorMessage);
4373
4354
  onError?.(errorMessage);
4374
4355
  Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
@@ -4408,8 +4389,11 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
4408
4389
  handleSuccess(response);
4409
4390
  return true;
4410
4391
  } catch (err) {
4411
- const errorMessage = err instanceof Error ? err.message : "Invalid backup code";
4412
4392
  authLogger.error("2FA backup code verification error:", err);
4393
+ const errorMessage = err instanceof APIError3 ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid backup code";
4394
+ if (err instanceof APIError3 && typeof err.response?.attempts_remaining === "number") {
4395
+ setAttemptsRemaining(err.response.attempts_remaining);
4396
+ }
4413
4397
  setError(errorMessage);
4414
4398
  onError?.(errorMessage);
4415
4399
  Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
@@ -4426,6 +4410,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
4426
4410
  error,
4427
4411
  warning,
4428
4412
  remainingBackupCodes,
4413
+ attemptsRemaining,
4429
4414
  verifyTOTP,
4430
4415
  verifyBackupCode,
4431
4416
  clearError
@@ -4446,20 +4431,15 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4446
4431
  } = options;
4447
4432
  const formState = useAuthFormState();
4448
4433
  const validation = useAuthValidation();
4449
- const isAutoSubmitFromUrlRef = useRef2(false);
4434
+ const isAutoSubmitFromUrlRef = useRef3(false);
4450
4435
  const {
4451
4436
  requestOTP,
4452
4437
  verifyOTP,
4453
4438
  getSavedEmail,
4454
- saveEmail,
4455
- clearSavedEmail,
4456
- getSavedPhone,
4457
- savePhone,
4458
- clearSavedPhone
4439
+ saveEmail
4459
4440
  } = useAuth();
4460
4441
  const {
4461
4442
  identifier,
4462
- channel,
4463
4443
  otp,
4464
4444
  isLoading,
4465
4445
  acceptedTerms,
@@ -4467,7 +4447,6 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4467
4447
  twoFactorCode,
4468
4448
  useBackupCode,
4469
4449
  setIdentifier,
4470
- setChannel,
4471
4450
  setOtp,
4472
4451
  setStep,
4473
4452
  setError,
@@ -4476,7 +4455,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4476
4455
  setTwoFactorSessionId,
4477
4456
  setShouldPrompt2FA,
4478
4457
  setTwoFactorCode,
4479
- setUseBackupCode
4458
+ setUseBackupCode,
4459
+ startRateLimitCountdown
4480
4460
  } = formState;
4481
4461
  const twoFactor = useTwoFactor({
4482
4462
  onSuccess: /* @__PURE__ */ __name(() => {
@@ -4492,45 +4472,26 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4492
4472
  skipRedirect: true
4493
4473
  // We handle navigation via success step
4494
4474
  });
4495
- const { detectChannelFromIdentifier: detectChannelFromIdentifier2, validateIdentifier: validateIdentifier2 } = validation;
4496
- const saveIdentifierToStorage = useCallback5((id, ch) => {
4497
- if (ch === "email") {
4498
- saveEmail(id);
4499
- clearSavedPhone();
4500
- } else {
4501
- savePhone(id);
4502
- clearSavedEmail();
4503
- }
4504
- }, [saveEmail, savePhone, clearSavedEmail, clearSavedPhone]);
4505
- useEffect3(() => {
4506
- const savedPhone = getSavedPhone();
4475
+ const { validateIdentifier: validateIdentifier2 } = validation;
4476
+ const saveIdentifierToStorage = useCallback5((id) => {
4477
+ saveEmail(id);
4478
+ }, [saveEmail]);
4479
+ useEffect4(() => {
4507
4480
  const savedEmail = getSavedEmail();
4508
- if (savedPhone) {
4509
- setIdentifier(savedPhone);
4510
- setChannel("phone");
4511
- } else if (savedEmail) {
4481
+ if (savedEmail) {
4512
4482
  setIdentifier(savedEmail);
4513
- setChannel("email");
4514
4483
  }
4515
- }, [getSavedEmail, getSavedPhone, setIdentifier, setChannel]);
4516
- useEffect3(() => {
4517
- if (identifier) {
4518
- const detected = detectChannelFromIdentifier2(identifier);
4519
- if (detected && detected !== channel) {
4520
- setChannel(detected);
4521
- }
4522
- }
4523
- }, [identifier, channel, detectChannelFromIdentifier2, setChannel]);
4484
+ }, []);
4524
4485
  const handleIdentifierSubmit = useCallback5(async (e) => {
4525
4486
  e.preventDefault();
4526
4487
  if (!identifier) {
4527
- const msg = channel === "phone" ? "Please enter your phone number" : "Please enter your email address";
4488
+ const msg = "Please enter your email address";
4528
4489
  setError(msg);
4529
4490
  onError?.(msg);
4530
4491
  return;
4531
4492
  }
4532
- if (!validateIdentifier2(identifier, channel)) {
4533
- const msg = channel === "phone" ? "Please enter a valid phone number (e.g., +1234567890)" : "Please enter a valid email address";
4493
+ if (!validateIdentifier2(identifier)) {
4494
+ const msg = "Please enter a valid email address";
4534
4495
  setError(msg);
4535
4496
  onError?.(msg);
4536
4497
  return;
@@ -4544,14 +4505,19 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4544
4505
  setIsLoading(true);
4545
4506
  clearError();
4546
4507
  try {
4547
- const result = await requestOTP(identifier, channel, sourceUrl);
4508
+ const result = await requestOTP(identifier, sourceUrl);
4548
4509
  if (result.success) {
4549
- saveIdentifierToStorage(identifier, channel);
4510
+ saveIdentifierToStorage(identifier);
4550
4511
  setStep("otp");
4551
- onIdentifierSuccess?.(identifier, channel);
4512
+ onIdentifierSuccess?.(identifier);
4552
4513
  } else {
4553
- setError(result.message);
4554
- onError?.(result.message);
4514
+ if (result.retryAfter) {
4515
+ startRateLimitCountdown(result.retryAfter);
4516
+ clearError();
4517
+ } else {
4518
+ setError(result.message);
4519
+ onError?.(result.message);
4520
+ }
4555
4521
  }
4556
4522
  } catch {
4557
4523
  const msg = "An unexpected error occurred";
@@ -4562,7 +4528,6 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4562
4528
  }
4563
4529
  }, [
4564
4530
  identifier,
4565
- channel,
4566
4531
  acceptedTerms,
4567
4532
  requireTermsAcceptance,
4568
4533
  validateIdentifier2,
@@ -4572,11 +4537,12 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4572
4537
  setIsLoading,
4573
4538
  setStep,
4574
4539
  clearError,
4540
+ startRateLimitCountdown,
4575
4541
  onIdentifierSuccess,
4576
4542
  onError,
4577
4543
  sourceUrl
4578
4544
  ]);
4579
- const submitOTP = useCallback5(async (submitIdentifier, submitOtp, submitChannel) => {
4545
+ const submitOTP = useCallback5(async (submitIdentifier, submitOtp) => {
4580
4546
  if (!submitOtp || submitOtp.length < 6) {
4581
4547
  const msg = "Please enter the 6-digit verification code";
4582
4548
  setError(msg);
@@ -4586,17 +4552,17 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4586
4552
  setIsLoading(true);
4587
4553
  clearError();
4588
4554
  try {
4589
- const result = await verifyOTP(submitIdentifier, submitOtp, submitChannel, sourceUrl, redirectUrl, true);
4555
+ const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true);
4590
4556
  if (result.requires_2fa && result.session_id) {
4591
4557
  authLogger.info("2FA required after OTP verification");
4592
4558
  setTwoFactorSessionId(result.session_id);
4593
4559
  setShouldPrompt2FA(result.should_prompt_2fa || false);
4594
4560
  setStep("2fa");
4595
- saveIdentifierToStorage(submitIdentifier, submitChannel);
4561
+ saveIdentifierToStorage(submitIdentifier);
4596
4562
  return true;
4597
4563
  }
4598
4564
  if (result.success) {
4599
- saveIdentifierToStorage(submitIdentifier, submitChannel);
4565
+ saveIdentifierToStorage(submitIdentifier);
4600
4566
  if (result.should_prompt_2fa && enable2FASetup) {
4601
4567
  authLogger.info("OTP verification successful, prompting 2FA setup");
4602
4568
  setShouldPrompt2FA(true);
@@ -4628,19 +4594,24 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4628
4594
  }, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep, enable2FASetup]);
4629
4595
  const handleOTPSubmit = useCallback5(async (e) => {
4630
4596
  e.preventDefault();
4631
- await submitOTP(identifier, otp, channel);
4632
- }, [identifier, otp, channel, submitOTP]);
4597
+ await submitOTP(identifier, otp);
4598
+ }, [identifier, otp, submitOTP]);
4633
4599
  const handleResendOTP = useCallback5(async () => {
4634
4600
  setIsLoading(true);
4635
4601
  clearError();
4636
4602
  try {
4637
- const result = await requestOTP(identifier, channel, sourceUrl);
4603
+ const result = await requestOTP(identifier, sourceUrl);
4638
4604
  if (result.success) {
4639
- saveIdentifierToStorage(identifier, channel);
4605
+ saveIdentifierToStorage(identifier);
4640
4606
  setOtp("");
4641
4607
  } else {
4642
- setError(result.message);
4643
- onError?.(result.message);
4608
+ if (result.retryAfter) {
4609
+ startRateLimitCountdown(result.retryAfter);
4610
+ clearError();
4611
+ } else {
4612
+ setError(result.message);
4613
+ onError?.(result.message);
4614
+ }
4644
4615
  }
4645
4616
  } catch {
4646
4617
  const msg = "Failed to resend verification code";
@@ -4649,7 +4620,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4649
4620
  } finally {
4650
4621
  setIsLoading(false);
4651
4622
  }
4652
- }, [identifier, channel, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, onError, sourceUrl]);
4623
+ }, [identifier, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, startRateLimitCountdown, onError, sourceUrl]);
4653
4624
  const handleBackToIdentifier = useCallback5(() => {
4654
4625
  setStep("identifier");
4655
4626
  clearError();
@@ -4688,29 +4659,19 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4688
4659
  if (isAutoSubmitFromUrlRef.current || isLoading) return;
4689
4660
  isAutoSubmitFromUrlRef.current = true;
4690
4661
  authLogger.info("OTP detected from URL, auto-submitting");
4691
- const savedPhone = getSavedPhone();
4692
4662
  const savedEmail = getSavedEmail();
4693
- let autoIdentifier = "";
4694
- let autoChannel = "email";
4695
- if (savedPhone) {
4696
- autoIdentifier = savedPhone;
4697
- autoChannel = "phone";
4698
- } else if (savedEmail) {
4699
- autoIdentifier = savedEmail;
4700
- autoChannel = "email";
4701
- }
4663
+ const autoIdentifier = savedEmail || "";
4702
4664
  if (!autoIdentifier) {
4703
4665
  authLogger.warn("No saved identifier found for auto-submit");
4704
4666
  isAutoSubmitFromUrlRef.current = false;
4705
4667
  return;
4706
4668
  }
4707
4669
  setIdentifier(autoIdentifier);
4708
- setChannel(autoChannel);
4709
4670
  setOtp(detectedOtp);
4710
4671
  setStep("otp");
4711
4672
  setTimeout(async () => {
4712
4673
  try {
4713
- await submitOTP(autoIdentifier, detectedOtp, autoChannel);
4674
+ await submitOTP(autoIdentifier, detectedOtp);
4714
4675
  } finally {
4715
4676
  isAutoSubmitFromUrlRef.current = false;
4716
4677
  }
@@ -4737,7 +4698,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
4737
4698
  isAutoSubmittingFromUrl: isAutoSubmitFromUrlRef,
4738
4699
  // 2FA state from hook (for loading indicator)
4739
4700
  is2FALoading: twoFactor.isLoading,
4740
- twoFactorWarning: twoFactor.warning
4701
+ twoFactorWarning: twoFactor.warning,
4702
+ twoFactorAttemptsRemaining: twoFactor.attemptsRemaining
4741
4703
  };
4742
4704
  }, "useAuthForm");
4743
4705
 
@@ -5221,13 +5183,13 @@ var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
5221
5183
  }, "useAuthRedirectManager");
5222
5184
 
5223
5185
  // src/auth/hooks/useAuthGuard.ts
5224
- import { useEffect as useEffect4, useState as useState8 } from "react";
5186
+ import { useEffect as useEffect5, useState as useState8 } from "react";
5225
5187
  var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
5226
5188
  const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
5227
5189
  const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
5228
5190
  const router = useCfgRouter();
5229
5191
  const [isRedirecting, setIsRedirecting] = useState8(false);
5230
- useEffect4(() => {
5192
+ useEffect5(() => {
5231
5193
  if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
5232
5194
  if (shouldSaveUrl && typeof window !== "undefined") {
5233
5195
  const currentUrl = window.location.pathname + window.location.search;
@@ -5540,7 +5502,7 @@ function getCacheMetadata() {
5540
5502
  __name(getCacheMetadata, "getCacheMetadata");
5541
5503
 
5542
5504
  // src/auth/hooks/useTokenRefresh.ts
5543
- import { useCallback as useCallback9, useEffect as useEffect6, useRef as useRef3 } from "react";
5505
+ import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef4 } from "react";
5544
5506
 
5545
5507
  // src/index.ts
5546
5508
  var isStaticBuild3 = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
@@ -5581,7 +5543,7 @@ function isTokenExpiringSoon(token, thresholdMs) {
5581
5543
  __name(isTokenExpiringSoon, "isTokenExpiringSoon");
5582
5544
  function useTokenRefresh(options = {}) {
5583
5545
  const { enabled = true, onRefresh, onRefreshError } = options;
5584
- const isRefreshingRef = useRef3(false);
5546
+ const isRefreshingRef = useRef4(false);
5585
5547
  const refreshToken = useCallback9(async () => {
5586
5548
  if (isRefreshingRef.current) {
5587
5549
  authLogger.debug("Token refresh already in progress");
@@ -5622,13 +5584,13 @@ function useTokenRefresh(options = {}) {
5622
5584
  await refreshToken();
5623
5585
  }
5624
5586
  }, [refreshToken]);
5625
- useEffect6(() => {
5587
+ useEffect7(() => {
5626
5588
  if (!enabled) return;
5627
5589
  checkAndRefresh();
5628
5590
  const intervalId = setInterval(checkAndRefresh, CHECK_INTERVAL_MS);
5629
5591
  return () => clearInterval(intervalId);
5630
5592
  }, [enabled, checkAndRefresh]);
5631
- useEffect6(() => {
5593
+ useEffect7(() => {
5632
5594
  if (!enabled) return;
5633
5595
  const handleFocus = /* @__PURE__ */ __name(() => {
5634
5596
  authLogger.debug("Window focused, checking token...");
@@ -5637,7 +5599,7 @@ function useTokenRefresh(options = {}) {
5637
5599
  window.addEventListener("focus", handleFocus);
5638
5600
  return () => window.removeEventListener("focus", handleFocus);
5639
5601
  }, [enabled, checkAndRefresh]);
5640
- useEffect6(() => {
5602
+ useEffect7(() => {
5641
5603
  if (!enabled) return;
5642
5604
  const handleOnline = /* @__PURE__ */ __name(() => {
5643
5605
  authLogger.info("Network reconnected, checking token...");
@@ -5695,8 +5657,8 @@ import {
5695
5657
  createContext,
5696
5658
  useCallback as useCallback11,
5697
5659
  useContext,
5698
- useEffect as useEffect7,
5699
- useRef as useRef4,
5660
+ useEffect as useEffect8,
5661
+ useRef as useRef5,
5700
5662
  useState as useState11
5701
5663
  } from "react";
5702
5664
  import { jsx } from "react/jsx-runtime";
@@ -5708,12 +5670,12 @@ function AccountsProvider({ children }) {
5708
5670
  });
5709
5671
  const [isLoadingProfile, setIsLoadingProfile] = useState11(false);
5710
5672
  const [profileError, setProfileError] = useState11(null);
5711
- const profileRef = useRef4(profile);
5712
- const isLoadingRef = useRef4(false);
5713
- useEffect7(() => {
5673
+ const profileRef = useRef5(profile);
5674
+ const isLoadingRef = useRef5(false);
5675
+ useEffect8(() => {
5714
5676
  profileRef.current = profile;
5715
5677
  }, [profile]);
5716
- useEffect7(() => {
5678
+ useEffect8(() => {
5717
5679
  isLoadingRef.current = isLoadingProfile;
5718
5680
  }, [isLoadingProfile]);
5719
5681
  const updateMutation = useUpdateAccountsProfileUpdateUpdate();
@@ -5775,7 +5737,11 @@ function AccountsProvider({ children }) {
5775
5737
  }
5776
5738
  if (result.access && result.refresh) {
5777
5739
  api.setToken(result.access, result.refresh);
5778
- await refreshProfile({ callerId: "verifyOTP", force: true });
5740
+ try {
5741
+ await refreshProfile({ callerId: "verifyOTP", force: true });
5742
+ } catch (profileError2) {
5743
+ authLogger.warn("Profile refresh failed after OTP verify (tokens are saved):", profileError2);
5744
+ }
5779
5745
  }
5780
5746
  return result;
5781
5747
  }, "verifyOTP");
@@ -5826,7 +5792,6 @@ var defaultRoutes = {
5826
5792
  };
5827
5793
  var AuthContext = createContext2(void 0);
5828
5794
  var EMAIL_STORAGE_KEY = "auth_email";
5829
- var PHONE_STORAGE_KEY = "auth_phone";
5830
5795
  var hasValidTokens = /* @__PURE__ */ __name(() => {
5831
5796
  if (typeof window === "undefined") return false;
5832
5797
  return api.isAuthenticated();
@@ -5849,7 +5814,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5849
5814
  const pathname = usePathname3();
5850
5815
  const queryParams = useQueryParams();
5851
5816
  const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage(EMAIL_STORAGE_KEY, null);
5852
- const [storedPhone, setStoredPhone, clearStoredPhone] = useLocalStorage(PHONE_STORAGE_KEY, null);
5853
5817
  useTokenRefresh({
5854
5818
  enabled: true,
5855
5819
  onRefresh: /* @__PURE__ */ __name((newToken) => {
@@ -5860,13 +5824,13 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5860
5824
  }, "onRefreshError")
5861
5825
  });
5862
5826
  const user = accounts.profile;
5863
- const userRef = useRef5(user);
5864
- const configRef = useRef5(config);
5865
- const isLoadingProfileRef = useRef5(false);
5866
- useEffect8(() => {
5827
+ const userRef = useRef6(user);
5828
+ const configRef = useRef6(config);
5829
+ const isLoadingProfileRef = useRef6(false);
5830
+ useEffect9(() => {
5867
5831
  userRef.current = user;
5868
5832
  }, [user]);
5869
- useEffect8(() => {
5833
+ useEffect9(() => {
5870
5834
  configRef.current = config;
5871
5835
  }, [config]);
5872
5836
  const clearAuthState = useCallback12((caller) => {
@@ -5888,7 +5852,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5888
5852
  }
5889
5853
  return false;
5890
5854
  }, [clearAuthState]);
5891
- const isAutoLoggingOutRef = useRef5(false);
5855
+ const isAutoLoggingOutRef = useRef6(false);
5892
5856
  const swrOnError = useCallback12((error) => {
5893
5857
  const isAuthError = error?.status === 401 || error?.statusCode === 401 || error?.code === "token_not_valid" || error?.code === "authentication_failed";
5894
5858
  if (isAuthError && !isAutoLoggingOutRef.current) {
@@ -5941,7 +5905,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5941
5905
  isLoadingProfileRef.current = false;
5942
5906
  }
5943
5907
  }, [clearAuthState, handleGlobalAuthError, accounts]);
5944
- useEffect8(() => {
5908
+ useEffect9(() => {
5945
5909
  if (initialized) return;
5946
5910
  const initializeAuth = /* @__PURE__ */ __name(async () => {
5947
5911
  authLogger.info("Initializing auth...");
@@ -5997,7 +5961,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
5997
5961
  }, "initializeAuth");
5998
5962
  initializeAuth();
5999
5963
  }, [initialized]);
6000
- useEffect8(() => {
5964
+ useEffect9(() => {
6001
5965
  if (!initialized) return;
6002
5966
  const isAuthenticated = api.isAuthenticated();
6003
5967
  const authRoute = config?.routes?.auth || defaultRoutes.auth;
@@ -6039,25 +6003,33 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6039
6003
  }
6040
6004
  }, [loadCurrentProfile, clearAuthState, pushToDefaultCallbackUrl, pushToDefaultAuthCallbackUrl, handleGlobalAuthError]);
6041
6005
  const requestOTP = useCallback12(
6042
- async (identifier, channel, sourceUrl) => {
6006
+ async (identifier, sourceUrl) => {
6043
6007
  api.clearTokens();
6044
6008
  try {
6045
- const channelValue = channel === "phone" ? enums_exports.OTPRequestRequestChannel.PHONE : enums_exports.OTPRequestRequestChannel.EMAIL;
6046
6009
  const result = await accounts.requestOTP({
6047
6010
  identifier,
6048
- channel: channelValue
6011
+ source_url: sourceUrl
6049
6012
  });
6050
- const channelName = channel === "phone" ? "phone number" : "email address";
6051
6013
  Analytics.event("auth_otp_request" /* AUTH_OTP_REQUEST */, {
6052
6014
  category: "auth" /* AUTH */,
6053
- label: channel || "email"
6015
+ label: "email"
6054
6016
  });
6055
6017
  return {
6056
6018
  success: true,
6057
- message: result.message || `OTP code sent to your ${channelName}`
6019
+ message: result.message || `OTP code sent to your email address`
6058
6020
  };
6059
6021
  } catch (error) {
6060
6022
  authLogger.error("Request OTP error:", error);
6023
+ if (error instanceof APIError) {
6024
+ const retryAfter = error.response?.retry_after ?? error.response?.retryAfter;
6025
+ const message = error.response?.error || error.response?.detail || error.response?.message || error.errorMessage;
6026
+ return {
6027
+ success: false,
6028
+ statusCode: error.statusCode,
6029
+ message,
6030
+ retryAfter: typeof retryAfter === "number" ? retryAfter : void 0
6031
+ };
6032
+ }
6061
6033
  return {
6062
6034
  success: false,
6063
6035
  message: "Failed to send OTP"
@@ -6067,13 +6039,12 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6067
6039
  [accounts]
6068
6040
  );
6069
6041
  const verifyOTP = useCallback12(
6070
- async (identifier, otpCode, channel, sourceUrl, redirectUrl, skipRedirect) => {
6042
+ async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect) => {
6071
6043
  try {
6072
- const channelValue = channel === "phone" ? enums_exports.OTPRequestRequestChannel.PHONE : enums_exports.OTPRequestRequestChannel.EMAIL;
6073
6044
  const result = await accounts.verifyOTP({
6074
6045
  identifier,
6075
6046
  otp: otpCode,
6076
- channel: channelValue
6047
+ source_url: sourceUrl
6077
6048
  });
6078
6049
  if (result.requires_2fa && result.session_id) {
6079
6050
  authLogger.info("2FA required, session:", result.session_id);
@@ -6092,17 +6063,13 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6092
6063
  message: "Invalid OTP verification response"
6093
6064
  };
6094
6065
  }
6095
- if (channel === "phone") {
6096
- setStoredPhone(identifier);
6097
- clearStoredEmail();
6098
- } else if (identifier.includes("@")) {
6066
+ if (identifier.includes("@")) {
6099
6067
  setStoredEmail(identifier);
6100
- clearStoredPhone();
6101
6068
  }
6102
6069
  await new Promise((resolve) => setTimeout(resolve, 200));
6103
6070
  Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
6104
6071
  category: "auth" /* AUTH */,
6105
- label: channel || "email"
6072
+ label: "email"
6106
6073
  });
6107
6074
  if (result.user?.id) {
6108
6075
  Analytics.setUser(String(result.user.id));
@@ -6123,15 +6090,19 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6123
6090
  authLogger.error("Verify OTP error:", error);
6124
6091
  Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
6125
6092
  category: "auth" /* AUTH */,
6126
- label: channel || "email"
6093
+ label: "email"
6127
6094
  });
6095
+ if (error instanceof APIError) {
6096
+ const message = error.response?.error || error.response?.detail || error.response?.message || error.errorMessage;
6097
+ return { success: false, message };
6098
+ }
6128
6099
  return {
6129
6100
  success: false,
6130
6101
  message: "Failed to verify OTP"
6131
6102
  };
6132
6103
  }
6133
6104
  },
6134
- [setStoredEmail, setStoredPhone, clearStoredEmail, clearStoredPhone, config?.routes?.defaultCallback, accounts, router]
6105
+ [setStoredEmail, config?.routes?.defaultCallback, accounts, router]
6135
6106
  );
6136
6107
  const refreshToken = useCallback12(async () => {
6137
6108
  try {
@@ -6207,9 +6178,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6207
6178
  getSavedEmail: /* @__PURE__ */ __name(() => storedEmail, "getSavedEmail"),
6208
6179
  saveEmail: setStoredEmail,
6209
6180
  clearSavedEmail: clearStoredEmail,
6210
- getSavedPhone: /* @__PURE__ */ __name(() => storedPhone, "getSavedPhone"),
6211
- savePhone: setStoredPhone,
6212
- clearSavedPhone: clearStoredPhone,
6213
6181
  requestOTP,
6214
6182
  verifyOTP,
6215
6183
  refreshToken,
@@ -6232,9 +6200,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
6232
6200
  storedEmail,
6233
6201
  setStoredEmail,
6234
6202
  clearStoredEmail,
6235
- storedPhone,
6236
- setStoredPhone,
6237
- clearStoredPhone,
6238
6203
  requestOTP,
6239
6204
  verifyOTP,
6240
6205
  refreshToken,
@@ -6268,12 +6233,6 @@ var defaultAuthState = {
6268
6233
  }, "saveEmail"),
6269
6234
  clearSavedEmail: /* @__PURE__ */ __name(() => {
6270
6235
  }, "clearSavedEmail"),
6271
- getSavedPhone: /* @__PURE__ */ __name(() => null, "getSavedPhone"),
6272
- savePhone: /* @__PURE__ */ __name(() => {
6273
- authLogger.warn("useAuth: savePhone called outside AuthProvider");
6274
- }, "savePhone"),
6275
- clearSavedPhone: /* @__PURE__ */ __name(() => {
6276
- }, "clearSavedPhone"),
6277
6236
  requestOTP: /* @__PURE__ */ __name(async () => {
6278
6237
  authLogger.warn("useAuth: requestOTP called outside AuthProvider");
6279
6238
  return { success: false, message: "AuthProvider not available" };
@@ -6347,7 +6306,6 @@ export {
6347
6306
  authLogger,
6348
6307
  clearProfileCache,
6349
6308
  decodeBase64,
6350
- detectChannelFromIdentifier,
6351
6309
  encodeBase64,
6352
6310
  formatAuthError,
6353
6311
  getCacheMetadata,