@djangocfg/api 2.1.227 → 2.1.228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/dist/auth-server.cjs +4 -9
- package/dist/auth-server.cjs.map +1 -1
- package/dist/auth-server.mjs +4 -9
- package/dist/auth-server.mjs.map +1 -1
- package/dist/auth.cjs +120 -158
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +120 -177
- package/dist/auth.d.ts +120 -177
- package/dist/auth.mjs +149 -191
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +5 -11
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +218 -219
- package/dist/clients.d.ts +218 -219
- package/dist/clients.mjs +5 -11
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +4 -9
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +70 -91
- package/dist/hooks.d.ts +70 -91
- package/dist/hooks.mjs +4 -9
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +5 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +116 -106
- package/dist/index.d.ts +116 -106
- package/dist/index.mjs +5 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/_api/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts +24 -2
- package/src/_api/generated/cfg_accounts/_utils/schemas/OTPRequestRequest.schema.ts +0 -2
- package/src/_api/generated/cfg_accounts/_utils/schemas/OTPVerifyRequest.schema.ts +0 -2
- package/src/_api/generated/cfg_accounts/accounts/client.ts +1 -1
- package/src/_api/generated/cfg_accounts/accounts/models.ts +25 -26
- package/src/_api/generated/cfg_accounts/accounts__auth/models.ts +5 -5
- package/src/_api/generated/cfg_accounts/accounts__oauth/models.ts +42 -42
- package/src/_api/generated/cfg_accounts/accounts__user_profile/models.ts +23 -23
- package/src/_api/generated/cfg_accounts/enums.ts +0 -10
- package/src/_api/generated/cfg_accounts/schema.json +31 -25
- package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_admin_api/models.ts +57 -57
- package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_monitoring/models.ts +24 -24
- package/src/_api/generated/cfg_centrifugo/centrifugo__centrifugo_testing/models.ts +14 -14
- package/src/_api/generated/cfg_totp/totp__backup_codes/models.ts +14 -14
- package/src/_api/generated/cfg_totp/totp__totp_setup/models.ts +10 -10
- package/src/_api/generated/cfg_totp/totp__totp_verification/models.ts +8 -8
- package/src/auth/context/AccountsContext.tsx +6 -2
- package/src/auth/context/AuthContext.tsx +32 -39
- package/src/auth/context/types.ts +5 -9
- package/src/auth/hooks/index.ts +1 -1
- package/src/auth/hooks/useAuthForm.ts +42 -75
- package/src/auth/hooks/useAuthFormState.ts +35 -6
- package/src/auth/hooks/useAuthValidation.ts +5 -65
- package/src/auth/hooks/useTwoFactor.ts +17 -2
- package/src/auth/types/form.ts +25 -70
- package/src/auth/types/index.ts +2 -6
package/dist/auth.cjs
CHANGED
|
@@ -40,7 +40,6 @@ __export(auth_exports, {
|
|
|
40
40
|
authLogger: () => authLogger,
|
|
41
41
|
clearProfileCache: () => clearProfileCache,
|
|
42
42
|
decodeBase64: () => decodeBase64,
|
|
43
|
-
detectChannelFromIdentifier: () => detectChannelFromIdentifier,
|
|
44
43
|
encodeBase64: () => encodeBase64,
|
|
45
44
|
formatAuthError: () => formatAuthError,
|
|
46
45
|
getCacheMetadata: () => getCacheMetadata,
|
|
@@ -182,9 +181,13 @@ __name(useQueryParams, "useQueryParams");
|
|
|
182
181
|
|
|
183
182
|
// src/auth/hooks/useAuthFormState.ts
|
|
184
183
|
var import_react3 = require("react");
|
|
185
|
-
var
|
|
184
|
+
var formatCountdown = /* @__PURE__ */ __name((s) => {
|
|
185
|
+
if (s <= 0) return "";
|
|
186
|
+
const m = Math.floor(s / 60);
|
|
187
|
+
return m > 0 ? `${m}:${String(s % 60).padStart(2, "0")}` : `${s}s`;
|
|
188
|
+
}, "formatCountdown");
|
|
189
|
+
var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
|
|
186
190
|
const [identifier, setIdentifier] = (0, import_react3.useState)(initialIdentifier);
|
|
187
|
-
const [channel, setChannel] = (0, import_react3.useState)(initialChannel);
|
|
188
191
|
const [otp, setOtp] = (0, import_react3.useState)("");
|
|
189
192
|
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
190
193
|
const [acceptedTerms, setAcceptedTerms] = (0, import_react3.useState)(true);
|
|
@@ -194,11 +197,29 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
|
|
|
194
197
|
const [shouldPrompt2FA, setShouldPrompt2FA] = (0, import_react3.useState)(false);
|
|
195
198
|
const [twoFactorCode, setTwoFactorCode] = (0, import_react3.useState)("");
|
|
196
199
|
const [useBackupCode, setUseBackupCode] = (0, import_react3.useState)(false);
|
|
200
|
+
const [rateLimitSeconds, setRateLimitSeconds] = (0, import_react3.useState)(0);
|
|
201
|
+
const rateLimitTimerRef = (0, import_react3.useRef)(null);
|
|
202
|
+
const startRateLimitCountdown = (0, import_react3.useCallback)((seconds) => {
|
|
203
|
+
if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
|
|
204
|
+
setRateLimitSeconds(seconds);
|
|
205
|
+
rateLimitTimerRef.current = setInterval(() => {
|
|
206
|
+
setRateLimitSeconds((prev) => {
|
|
207
|
+
if (prev <= 1) {
|
|
208
|
+
clearInterval(rateLimitTimerRef.current);
|
|
209
|
+
rateLimitTimerRef.current = null;
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
return prev - 1;
|
|
213
|
+
});
|
|
214
|
+
}, 1e3);
|
|
215
|
+
}, []);
|
|
216
|
+
(0, import_react3.useEffect)(() => () => {
|
|
217
|
+
if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
|
|
218
|
+
}, []);
|
|
197
219
|
const clearError = (0, import_react3.useCallback)(() => setError(""), []);
|
|
198
220
|
return {
|
|
199
221
|
// State
|
|
200
222
|
identifier,
|
|
201
|
-
channel,
|
|
202
223
|
otp,
|
|
203
224
|
isLoading,
|
|
204
225
|
acceptedTerms,
|
|
@@ -208,9 +229,11 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
|
|
|
208
229
|
shouldPrompt2FA,
|
|
209
230
|
twoFactorCode,
|
|
210
231
|
useBackupCode,
|
|
232
|
+
rateLimitSeconds,
|
|
233
|
+
isRateLimited: rateLimitSeconds > 0,
|
|
234
|
+
rateLimitLabel: formatCountdown(rateLimitSeconds),
|
|
211
235
|
// Handlers
|
|
212
236
|
setIdentifier,
|
|
213
|
-
setChannel,
|
|
214
237
|
setOtp,
|
|
215
238
|
setAcceptedTerms,
|
|
216
239
|
setError,
|
|
@@ -220,54 +243,21 @@ var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "", initialCh
|
|
|
220
243
|
setTwoFactorSessionId,
|
|
221
244
|
setShouldPrompt2FA,
|
|
222
245
|
setTwoFactorCode,
|
|
223
|
-
setUseBackupCode
|
|
246
|
+
setUseBackupCode,
|
|
247
|
+
startRateLimitCountdown
|
|
224
248
|
};
|
|
225
249
|
}, "useAuthFormState");
|
|
226
250
|
|
|
227
251
|
// src/auth/hooks/useAuthValidation.ts
|
|
228
252
|
var import_react4 = require("react");
|
|
229
253
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
230
|
-
var PHONE_REGEX = /^\+[1-9]\d{6,14}$/;
|
|
231
254
|
var useAuthValidation = /* @__PURE__ */ __name(() => {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
if (id.includes("@")) {
|
|
235
|
-
return "email";
|
|
236
|
-
}
|
|
237
|
-
if (id.startsWith("+") && PHONE_REGEX.test(id)) {
|
|
238
|
-
return "phone";
|
|
239
|
-
}
|
|
240
|
-
return null;
|
|
255
|
+
const validateIdentifier2 = (0, import_react4.useCallback)((id) => {
|
|
256
|
+
return EMAIL_REGEX.test(id);
|
|
241
257
|
}, []);
|
|
242
|
-
|
|
243
|
-
if (!id) return false;
|
|
244
|
-
const channel = channelType || detectChannelFromIdentifier2(id);
|
|
245
|
-
if (channel === "email") {
|
|
246
|
-
return EMAIL_REGEX.test(id);
|
|
247
|
-
}
|
|
248
|
-
if (channel === "phone") {
|
|
249
|
-
return PHONE_REGEX.test(id);
|
|
250
|
-
}
|
|
251
|
-
return false;
|
|
252
|
-
}, [detectChannelFromIdentifier2]);
|
|
253
|
-
return {
|
|
254
|
-
detectChannelFromIdentifier: detectChannelFromIdentifier2,
|
|
255
|
-
validateIdentifier: validateIdentifier2
|
|
256
|
-
};
|
|
258
|
+
return { validateIdentifier: validateIdentifier2 };
|
|
257
259
|
}, "useAuthValidation");
|
|
258
|
-
var
|
|
259
|
-
if (!id) return null;
|
|
260
|
-
if (id.includes("@")) return "email";
|
|
261
|
-
if (id.startsWith("+") && PHONE_REGEX.test(id)) return "phone";
|
|
262
|
-
return null;
|
|
263
|
-
}, "detectChannelFromIdentifier");
|
|
264
|
-
var validateIdentifier = /* @__PURE__ */ __name((id, channelType) => {
|
|
265
|
-
if (!id) return false;
|
|
266
|
-
const channel = channelType || detectChannelFromIdentifier(id);
|
|
267
|
-
if (channel === "email") return EMAIL_REGEX.test(id);
|
|
268
|
-
if (channel === "phone") return PHONE_REGEX.test(id);
|
|
269
|
-
return false;
|
|
270
|
-
}, "validateIdentifier");
|
|
260
|
+
var validateIdentifier = /* @__PURE__ */ __name((id) => EMAIL_REGEX.test(id), "validateIdentifier");
|
|
271
261
|
|
|
272
262
|
// src/auth/hooks/useAuthForm.ts
|
|
273
263
|
var import_react7 = require("react");
|
|
@@ -483,7 +473,7 @@ var Accounts = class {
|
|
|
483
473
|
this.client = client;
|
|
484
474
|
}
|
|
485
475
|
/**
|
|
486
|
-
* Request OTP code to email
|
|
476
|
+
* Request OTP code to email.
|
|
487
477
|
*/
|
|
488
478
|
async otpRequestCreate(data) {
|
|
489
479
|
const response = await this.client.request("POST", "/cfg/accounts/otp/request/", { body: data });
|
|
@@ -1124,20 +1114,10 @@ var LocalStorageAdapter = class {
|
|
|
1124
1114
|
};
|
|
1125
1115
|
|
|
1126
1116
|
// src/_api/generated/cfg_accounts/enums.ts
|
|
1127
|
-
var enums_exports = {};
|
|
1128
|
-
__export(enums_exports, {
|
|
1129
|
-
OAuthConnectionProvider: () => OAuthConnectionProvider,
|
|
1130
|
-
OTPRequestRequestChannel: () => OTPRequestRequestChannel
|
|
1131
|
-
});
|
|
1132
1117
|
var OAuthConnectionProvider = /* @__PURE__ */ ((OAuthConnectionProvider2) => {
|
|
1133
1118
|
OAuthConnectionProvider2["GITHUB"] = "github";
|
|
1134
1119
|
return OAuthConnectionProvider2;
|
|
1135
1120
|
})(OAuthConnectionProvider || {});
|
|
1136
|
-
var OTPRequestRequestChannel = /* @__PURE__ */ ((OTPRequestRequestChannel2) => {
|
|
1137
|
-
OTPRequestRequestChannel2["EMAIL"] = "email";
|
|
1138
|
-
OTPRequestRequestChannel2["PHONE"] = "phone";
|
|
1139
|
-
return OTPRequestRequestChannel2;
|
|
1140
|
-
})(OTPRequestRequestChannel || {});
|
|
1141
1121
|
|
|
1142
1122
|
// src/_api/generated/cfg_accounts/_utils/schemas/AccountDeleteResponse.schema.ts
|
|
1143
1123
|
var import_zod = require("zod");
|
|
@@ -1231,14 +1211,15 @@ var OAuthTokenResponseSchema = import_zod11.z.object({
|
|
|
1231
1211
|
// src/_api/generated/cfg_accounts/_utils/schemas/OTPErrorResponse.schema.ts
|
|
1232
1212
|
var import_zod12 = require("zod");
|
|
1233
1213
|
var OTPErrorResponseSchema = import_zod12.z.object({
|
|
1234
|
-
error: import_zod12.z.string()
|
|
1214
|
+
error: import_zod12.z.string(),
|
|
1215
|
+
error_code: import_zod12.z.string().nullable().optional(),
|
|
1216
|
+
retry_after: import_zod12.z.number().int().nullable().optional()
|
|
1235
1217
|
});
|
|
1236
1218
|
|
|
1237
1219
|
// src/_api/generated/cfg_accounts/_utils/schemas/OTPRequestRequest.schema.ts
|
|
1238
1220
|
var import_zod13 = require("zod");
|
|
1239
1221
|
var OTPRequestRequestSchema = import_zod13.z.object({
|
|
1240
1222
|
identifier: import_zod13.z.string().min(1),
|
|
1241
|
-
channel: import_zod13.z.nativeEnum(OTPRequestRequestChannel).optional(),
|
|
1242
1223
|
source_url: import_zod13.z.string().optional()
|
|
1243
1224
|
});
|
|
1244
1225
|
|
|
@@ -1253,7 +1234,6 @@ var import_zod15 = require("zod");
|
|
|
1253
1234
|
var OTPVerifyRequestSchema = import_zod15.z.object({
|
|
1254
1235
|
identifier: import_zod15.z.string().min(1),
|
|
1255
1236
|
otp: import_zod15.z.string().min(6).max(6),
|
|
1256
|
-
channel: import_zod15.z.nativeEnum(OTPRequestRequestChannel).optional(),
|
|
1257
1237
|
source_url: import_zod15.z.string().optional()
|
|
1258
1238
|
});
|
|
1259
1239
|
|
|
@@ -4373,6 +4353,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
4373
4353
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
4374
4354
|
const [warning, setWarning] = (0, import_react6.useState)(null);
|
|
4375
4355
|
const [remainingBackupCodes, setRemainingBackupCodes] = (0, import_react6.useState)(null);
|
|
4356
|
+
const [attemptsRemaining, setAttemptsRemaining] = (0, import_react6.useState)(null);
|
|
4376
4357
|
const clearError = (0, import_react6.useCallback)(() => {
|
|
4377
4358
|
setError(null);
|
|
4378
4359
|
}, []);
|
|
@@ -4425,8 +4406,11 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
4425
4406
|
handleSuccess(response);
|
|
4426
4407
|
return true;
|
|
4427
4408
|
} catch (err) {
|
|
4428
|
-
const errorMessage = err instanceof Error ? err.message : "Invalid verification code";
|
|
4429
4409
|
authLogger.error("2FA TOTP verification error:", err);
|
|
4410
|
+
const errorMessage = err instanceof APIError3 ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid verification code";
|
|
4411
|
+
if (err instanceof APIError3 && typeof err.response?.attempts_remaining === "number") {
|
|
4412
|
+
setAttemptsRemaining(err.response.attempts_remaining);
|
|
4413
|
+
}
|
|
4430
4414
|
setError(errorMessage);
|
|
4431
4415
|
onError?.(errorMessage);
|
|
4432
4416
|
Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
|
|
@@ -4466,8 +4450,11 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
4466
4450
|
handleSuccess(response);
|
|
4467
4451
|
return true;
|
|
4468
4452
|
} catch (err) {
|
|
4469
|
-
const errorMessage = err instanceof Error ? err.message : "Invalid backup code";
|
|
4470
4453
|
authLogger.error("2FA backup code verification error:", err);
|
|
4454
|
+
const errorMessage = err instanceof APIError3 ? err.response?.error || err.response?.detail || err.response?.message || err.errorMessage : err instanceof Error ? err.message : "Invalid backup code";
|
|
4455
|
+
if (err instanceof APIError3 && typeof err.response?.attempts_remaining === "number") {
|
|
4456
|
+
setAttemptsRemaining(err.response.attempts_remaining);
|
|
4457
|
+
}
|
|
4471
4458
|
setError(errorMessage);
|
|
4472
4459
|
onError?.(errorMessage);
|
|
4473
4460
|
Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
|
|
@@ -4484,6 +4471,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
4484
4471
|
error,
|
|
4485
4472
|
warning,
|
|
4486
4473
|
remainingBackupCodes,
|
|
4474
|
+
attemptsRemaining,
|
|
4487
4475
|
verifyTOTP,
|
|
4488
4476
|
verifyBackupCode,
|
|
4489
4477
|
clearError
|
|
@@ -4509,15 +4497,10 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4509
4497
|
requestOTP,
|
|
4510
4498
|
verifyOTP,
|
|
4511
4499
|
getSavedEmail,
|
|
4512
|
-
saveEmail
|
|
4513
|
-
clearSavedEmail,
|
|
4514
|
-
getSavedPhone,
|
|
4515
|
-
savePhone,
|
|
4516
|
-
clearSavedPhone
|
|
4500
|
+
saveEmail
|
|
4517
4501
|
} = useAuth();
|
|
4518
4502
|
const {
|
|
4519
4503
|
identifier,
|
|
4520
|
-
channel,
|
|
4521
4504
|
otp,
|
|
4522
4505
|
isLoading,
|
|
4523
4506
|
acceptedTerms,
|
|
@@ -4525,7 +4508,6 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4525
4508
|
twoFactorCode,
|
|
4526
4509
|
useBackupCode,
|
|
4527
4510
|
setIdentifier,
|
|
4528
|
-
setChannel,
|
|
4529
4511
|
setOtp,
|
|
4530
4512
|
setStep,
|
|
4531
4513
|
setError,
|
|
@@ -4534,7 +4516,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4534
4516
|
setTwoFactorSessionId,
|
|
4535
4517
|
setShouldPrompt2FA,
|
|
4536
4518
|
setTwoFactorCode,
|
|
4537
|
-
setUseBackupCode
|
|
4519
|
+
setUseBackupCode,
|
|
4520
|
+
startRateLimitCountdown
|
|
4538
4521
|
} = formState;
|
|
4539
4522
|
const twoFactor = useTwoFactor({
|
|
4540
4523
|
onSuccess: /* @__PURE__ */ __name(() => {
|
|
@@ -4550,45 +4533,26 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4550
4533
|
skipRedirect: true
|
|
4551
4534
|
// We handle navigation via success step
|
|
4552
4535
|
});
|
|
4553
|
-
const {
|
|
4554
|
-
const saveIdentifierToStorage = (0, import_react7.useCallback)((id
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
clearSavedPhone();
|
|
4558
|
-
} else {
|
|
4559
|
-
savePhone(id);
|
|
4560
|
-
clearSavedEmail();
|
|
4561
|
-
}
|
|
4562
|
-
}, [saveEmail, savePhone, clearSavedEmail, clearSavedPhone]);
|
|
4536
|
+
const { validateIdentifier: validateIdentifier2 } = validation;
|
|
4537
|
+
const saveIdentifierToStorage = (0, import_react7.useCallback)((id) => {
|
|
4538
|
+
saveEmail(id);
|
|
4539
|
+
}, [saveEmail]);
|
|
4563
4540
|
(0, import_react7.useEffect)(() => {
|
|
4564
|
-
const savedPhone = getSavedPhone();
|
|
4565
4541
|
const savedEmail = getSavedEmail();
|
|
4566
|
-
if (
|
|
4567
|
-
setIdentifier(savedPhone);
|
|
4568
|
-
setChannel("phone");
|
|
4569
|
-
} else if (savedEmail) {
|
|
4542
|
+
if (savedEmail) {
|
|
4570
4543
|
setIdentifier(savedEmail);
|
|
4571
|
-
setChannel("email");
|
|
4572
4544
|
}
|
|
4573
|
-
}, [
|
|
4574
|
-
(0, import_react7.useEffect)(() => {
|
|
4575
|
-
if (identifier) {
|
|
4576
|
-
const detected = detectChannelFromIdentifier2(identifier);
|
|
4577
|
-
if (detected && detected !== channel) {
|
|
4578
|
-
setChannel(detected);
|
|
4579
|
-
}
|
|
4580
|
-
}
|
|
4581
|
-
}, [identifier, channel, detectChannelFromIdentifier2, setChannel]);
|
|
4545
|
+
}, []);
|
|
4582
4546
|
const handleIdentifierSubmit = (0, import_react7.useCallback)(async (e) => {
|
|
4583
4547
|
e.preventDefault();
|
|
4584
4548
|
if (!identifier) {
|
|
4585
|
-
const msg =
|
|
4549
|
+
const msg = "Please enter your email address";
|
|
4586
4550
|
setError(msg);
|
|
4587
4551
|
onError?.(msg);
|
|
4588
4552
|
return;
|
|
4589
4553
|
}
|
|
4590
|
-
if (!validateIdentifier2(identifier
|
|
4591
|
-
const msg =
|
|
4554
|
+
if (!validateIdentifier2(identifier)) {
|
|
4555
|
+
const msg = "Please enter a valid email address";
|
|
4592
4556
|
setError(msg);
|
|
4593
4557
|
onError?.(msg);
|
|
4594
4558
|
return;
|
|
@@ -4602,14 +4566,19 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4602
4566
|
setIsLoading(true);
|
|
4603
4567
|
clearError();
|
|
4604
4568
|
try {
|
|
4605
|
-
const result = await requestOTP(identifier,
|
|
4569
|
+
const result = await requestOTP(identifier, sourceUrl);
|
|
4606
4570
|
if (result.success) {
|
|
4607
|
-
saveIdentifierToStorage(identifier
|
|
4571
|
+
saveIdentifierToStorage(identifier);
|
|
4608
4572
|
setStep("otp");
|
|
4609
|
-
onIdentifierSuccess?.(identifier
|
|
4573
|
+
onIdentifierSuccess?.(identifier);
|
|
4610
4574
|
} else {
|
|
4611
|
-
|
|
4612
|
-
|
|
4575
|
+
if (result.retryAfter) {
|
|
4576
|
+
startRateLimitCountdown(result.retryAfter);
|
|
4577
|
+
clearError();
|
|
4578
|
+
} else {
|
|
4579
|
+
setError(result.message);
|
|
4580
|
+
onError?.(result.message);
|
|
4581
|
+
}
|
|
4613
4582
|
}
|
|
4614
4583
|
} catch {
|
|
4615
4584
|
const msg = "An unexpected error occurred";
|
|
@@ -4620,7 +4589,6 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4620
4589
|
}
|
|
4621
4590
|
}, [
|
|
4622
4591
|
identifier,
|
|
4623
|
-
channel,
|
|
4624
4592
|
acceptedTerms,
|
|
4625
4593
|
requireTermsAcceptance,
|
|
4626
4594
|
validateIdentifier2,
|
|
@@ -4630,11 +4598,12 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4630
4598
|
setIsLoading,
|
|
4631
4599
|
setStep,
|
|
4632
4600
|
clearError,
|
|
4601
|
+
startRateLimitCountdown,
|
|
4633
4602
|
onIdentifierSuccess,
|
|
4634
4603
|
onError,
|
|
4635
4604
|
sourceUrl
|
|
4636
4605
|
]);
|
|
4637
|
-
const submitOTP = (0, import_react7.useCallback)(async (submitIdentifier, submitOtp
|
|
4606
|
+
const submitOTP = (0, import_react7.useCallback)(async (submitIdentifier, submitOtp) => {
|
|
4638
4607
|
if (!submitOtp || submitOtp.length < 6) {
|
|
4639
4608
|
const msg = "Please enter the 6-digit verification code";
|
|
4640
4609
|
setError(msg);
|
|
@@ -4644,17 +4613,17 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4644
4613
|
setIsLoading(true);
|
|
4645
4614
|
clearError();
|
|
4646
4615
|
try {
|
|
4647
|
-
const result = await verifyOTP(submitIdentifier, submitOtp,
|
|
4616
|
+
const result = await verifyOTP(submitIdentifier, submitOtp, sourceUrl, redirectUrl, true);
|
|
4648
4617
|
if (result.requires_2fa && result.session_id) {
|
|
4649
4618
|
authLogger.info("2FA required after OTP verification");
|
|
4650
4619
|
setTwoFactorSessionId(result.session_id);
|
|
4651
4620
|
setShouldPrompt2FA(result.should_prompt_2fa || false);
|
|
4652
4621
|
setStep("2fa");
|
|
4653
|
-
saveIdentifierToStorage(submitIdentifier
|
|
4622
|
+
saveIdentifierToStorage(submitIdentifier);
|
|
4654
4623
|
return true;
|
|
4655
4624
|
}
|
|
4656
4625
|
if (result.success) {
|
|
4657
|
-
saveIdentifierToStorage(submitIdentifier
|
|
4626
|
+
saveIdentifierToStorage(submitIdentifier);
|
|
4658
4627
|
if (result.should_prompt_2fa && enable2FASetup) {
|
|
4659
4628
|
authLogger.info("OTP verification successful, prompting 2FA setup");
|
|
4660
4629
|
setShouldPrompt2FA(true);
|
|
@@ -4686,19 +4655,24 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4686
4655
|
}, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep, enable2FASetup]);
|
|
4687
4656
|
const handleOTPSubmit = (0, import_react7.useCallback)(async (e) => {
|
|
4688
4657
|
e.preventDefault();
|
|
4689
|
-
await submitOTP(identifier, otp
|
|
4690
|
-
}, [identifier, otp,
|
|
4658
|
+
await submitOTP(identifier, otp);
|
|
4659
|
+
}, [identifier, otp, submitOTP]);
|
|
4691
4660
|
const handleResendOTP = (0, import_react7.useCallback)(async () => {
|
|
4692
4661
|
setIsLoading(true);
|
|
4693
4662
|
clearError();
|
|
4694
4663
|
try {
|
|
4695
|
-
const result = await requestOTP(identifier,
|
|
4664
|
+
const result = await requestOTP(identifier, sourceUrl);
|
|
4696
4665
|
if (result.success) {
|
|
4697
|
-
saveIdentifierToStorage(identifier
|
|
4666
|
+
saveIdentifierToStorage(identifier);
|
|
4698
4667
|
setOtp("");
|
|
4699
4668
|
} else {
|
|
4700
|
-
|
|
4701
|
-
|
|
4669
|
+
if (result.retryAfter) {
|
|
4670
|
+
startRateLimitCountdown(result.retryAfter);
|
|
4671
|
+
clearError();
|
|
4672
|
+
} else {
|
|
4673
|
+
setError(result.message);
|
|
4674
|
+
onError?.(result.message);
|
|
4675
|
+
}
|
|
4702
4676
|
}
|
|
4703
4677
|
} catch {
|
|
4704
4678
|
const msg = "Failed to resend verification code";
|
|
@@ -4707,7 +4681,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4707
4681
|
} finally {
|
|
4708
4682
|
setIsLoading(false);
|
|
4709
4683
|
}
|
|
4710
|
-
}, [identifier,
|
|
4684
|
+
}, [identifier, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, startRateLimitCountdown, onError, sourceUrl]);
|
|
4711
4685
|
const handleBackToIdentifier = (0, import_react7.useCallback)(() => {
|
|
4712
4686
|
setStep("identifier");
|
|
4713
4687
|
clearError();
|
|
@@ -4746,29 +4720,19 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4746
4720
|
if (isAutoSubmitFromUrlRef.current || isLoading) return;
|
|
4747
4721
|
isAutoSubmitFromUrlRef.current = true;
|
|
4748
4722
|
authLogger.info("OTP detected from URL, auto-submitting");
|
|
4749
|
-
const savedPhone = getSavedPhone();
|
|
4750
4723
|
const savedEmail = getSavedEmail();
|
|
4751
|
-
|
|
4752
|
-
let autoChannel = "email";
|
|
4753
|
-
if (savedPhone) {
|
|
4754
|
-
autoIdentifier = savedPhone;
|
|
4755
|
-
autoChannel = "phone";
|
|
4756
|
-
} else if (savedEmail) {
|
|
4757
|
-
autoIdentifier = savedEmail;
|
|
4758
|
-
autoChannel = "email";
|
|
4759
|
-
}
|
|
4724
|
+
const autoIdentifier = savedEmail || "";
|
|
4760
4725
|
if (!autoIdentifier) {
|
|
4761
4726
|
authLogger.warn("No saved identifier found for auto-submit");
|
|
4762
4727
|
isAutoSubmitFromUrlRef.current = false;
|
|
4763
4728
|
return;
|
|
4764
4729
|
}
|
|
4765
4730
|
setIdentifier(autoIdentifier);
|
|
4766
|
-
setChannel(autoChannel);
|
|
4767
4731
|
setOtp(detectedOtp);
|
|
4768
4732
|
setStep("otp");
|
|
4769
4733
|
setTimeout(async () => {
|
|
4770
4734
|
try {
|
|
4771
|
-
await submitOTP(autoIdentifier, detectedOtp
|
|
4735
|
+
await submitOTP(autoIdentifier, detectedOtp);
|
|
4772
4736
|
} finally {
|
|
4773
4737
|
isAutoSubmitFromUrlRef.current = false;
|
|
4774
4738
|
}
|
|
@@ -4795,7 +4759,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
4795
4759
|
isAutoSubmittingFromUrl: isAutoSubmitFromUrlRef,
|
|
4796
4760
|
// 2FA state from hook (for loading indicator)
|
|
4797
4761
|
is2FALoading: twoFactor.isLoading,
|
|
4798
|
-
twoFactorWarning: twoFactor.warning
|
|
4762
|
+
twoFactorWarning: twoFactor.warning,
|
|
4763
|
+
twoFactorAttemptsRemaining: twoFactor.attemptsRemaining
|
|
4799
4764
|
};
|
|
4800
4765
|
}, "useAuthForm");
|
|
4801
4766
|
|
|
@@ -5826,7 +5791,11 @@ function AccountsProvider({ children }) {
|
|
|
5826
5791
|
}
|
|
5827
5792
|
if (result.access && result.refresh) {
|
|
5828
5793
|
api.setToken(result.access, result.refresh);
|
|
5829
|
-
|
|
5794
|
+
try {
|
|
5795
|
+
await refreshProfile({ callerId: "verifyOTP", force: true });
|
|
5796
|
+
} catch (profileError2) {
|
|
5797
|
+
authLogger.warn("Profile refresh failed after OTP verify (tokens are saved):", profileError2);
|
|
5798
|
+
}
|
|
5830
5799
|
}
|
|
5831
5800
|
return result;
|
|
5832
5801
|
}, "verifyOTP");
|
|
@@ -5877,7 +5846,6 @@ var defaultRoutes = {
|
|
|
5877
5846
|
};
|
|
5878
5847
|
var AuthContext = (0, import_react17.createContext)(void 0);
|
|
5879
5848
|
var EMAIL_STORAGE_KEY = "auth_email";
|
|
5880
|
-
var PHONE_STORAGE_KEY = "auth_phone";
|
|
5881
5849
|
var hasValidTokens = /* @__PURE__ */ __name(() => {
|
|
5882
5850
|
if (typeof window === "undefined") return false;
|
|
5883
5851
|
return api.isAuthenticated();
|
|
@@ -5900,7 +5868,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5900
5868
|
const pathname = (0, import_navigation4.usePathname)();
|
|
5901
5869
|
const queryParams = useQueryParams();
|
|
5902
5870
|
const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage(EMAIL_STORAGE_KEY, null);
|
|
5903
|
-
const [storedPhone, setStoredPhone, clearStoredPhone] = useLocalStorage(PHONE_STORAGE_KEY, null);
|
|
5904
5871
|
useTokenRefresh({
|
|
5905
5872
|
enabled: true,
|
|
5906
5873
|
onRefresh: /* @__PURE__ */ __name((newToken) => {
|
|
@@ -6090,25 +6057,33 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6090
6057
|
}
|
|
6091
6058
|
}, [loadCurrentProfile, clearAuthState, pushToDefaultCallbackUrl, pushToDefaultAuthCallbackUrl, handleGlobalAuthError]);
|
|
6092
6059
|
const requestOTP = (0, import_react17.useCallback)(
|
|
6093
|
-
async (identifier,
|
|
6060
|
+
async (identifier, sourceUrl) => {
|
|
6094
6061
|
api.clearTokens();
|
|
6095
6062
|
try {
|
|
6096
|
-
const channelValue = channel === "phone" ? enums_exports.OTPRequestRequestChannel.PHONE : enums_exports.OTPRequestRequestChannel.EMAIL;
|
|
6097
6063
|
const result = await accounts.requestOTP({
|
|
6098
6064
|
identifier,
|
|
6099
|
-
|
|
6065
|
+
source_url: sourceUrl
|
|
6100
6066
|
});
|
|
6101
|
-
const channelName = channel === "phone" ? "phone number" : "email address";
|
|
6102
6067
|
Analytics.event("auth_otp_request" /* AUTH_OTP_REQUEST */, {
|
|
6103
6068
|
category: "auth" /* AUTH */,
|
|
6104
|
-
label:
|
|
6069
|
+
label: "email"
|
|
6105
6070
|
});
|
|
6106
6071
|
return {
|
|
6107
6072
|
success: true,
|
|
6108
|
-
message: result.message || `OTP code sent to your
|
|
6073
|
+
message: result.message || `OTP code sent to your email address`
|
|
6109
6074
|
};
|
|
6110
6075
|
} catch (error) {
|
|
6111
6076
|
authLogger.error("Request OTP error:", error);
|
|
6077
|
+
if (error instanceof APIError) {
|
|
6078
|
+
const retryAfter = error.response?.retry_after ?? error.response?.retryAfter;
|
|
6079
|
+
const message = error.response?.error || error.response?.detail || error.response?.message || error.errorMessage;
|
|
6080
|
+
return {
|
|
6081
|
+
success: false,
|
|
6082
|
+
statusCode: error.statusCode,
|
|
6083
|
+
message,
|
|
6084
|
+
retryAfter: typeof retryAfter === "number" ? retryAfter : void 0
|
|
6085
|
+
};
|
|
6086
|
+
}
|
|
6112
6087
|
return {
|
|
6113
6088
|
success: false,
|
|
6114
6089
|
message: "Failed to send OTP"
|
|
@@ -6118,13 +6093,12 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6118
6093
|
[accounts]
|
|
6119
6094
|
);
|
|
6120
6095
|
const verifyOTP = (0, import_react17.useCallback)(
|
|
6121
|
-
async (identifier, otpCode,
|
|
6096
|
+
async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect) => {
|
|
6122
6097
|
try {
|
|
6123
|
-
const channelValue = channel === "phone" ? enums_exports.OTPRequestRequestChannel.PHONE : enums_exports.OTPRequestRequestChannel.EMAIL;
|
|
6124
6098
|
const result = await accounts.verifyOTP({
|
|
6125
6099
|
identifier,
|
|
6126
6100
|
otp: otpCode,
|
|
6127
|
-
|
|
6101
|
+
source_url: sourceUrl
|
|
6128
6102
|
});
|
|
6129
6103
|
if (result.requires_2fa && result.session_id) {
|
|
6130
6104
|
authLogger.info("2FA required, session:", result.session_id);
|
|
@@ -6143,17 +6117,13 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6143
6117
|
message: "Invalid OTP verification response"
|
|
6144
6118
|
};
|
|
6145
6119
|
}
|
|
6146
|
-
if (
|
|
6147
|
-
setStoredPhone(identifier);
|
|
6148
|
-
clearStoredEmail();
|
|
6149
|
-
} else if (identifier.includes("@")) {
|
|
6120
|
+
if (identifier.includes("@")) {
|
|
6150
6121
|
setStoredEmail(identifier);
|
|
6151
|
-
clearStoredPhone();
|
|
6152
6122
|
}
|
|
6153
6123
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
6154
6124
|
Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
|
|
6155
6125
|
category: "auth" /* AUTH */,
|
|
6156
|
-
label:
|
|
6126
|
+
label: "email"
|
|
6157
6127
|
});
|
|
6158
6128
|
if (result.user?.id) {
|
|
6159
6129
|
Analytics.setUser(String(result.user.id));
|
|
@@ -6174,15 +6144,19 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6174
6144
|
authLogger.error("Verify OTP error:", error);
|
|
6175
6145
|
Analytics.event("auth_otp_verify_fail" /* AUTH_OTP_VERIFY_FAIL */, {
|
|
6176
6146
|
category: "auth" /* AUTH */,
|
|
6177
|
-
label:
|
|
6147
|
+
label: "email"
|
|
6178
6148
|
});
|
|
6149
|
+
if (error instanceof APIError) {
|
|
6150
|
+
const message = error.response?.error || error.response?.detail || error.response?.message || error.errorMessage;
|
|
6151
|
+
return { success: false, message };
|
|
6152
|
+
}
|
|
6179
6153
|
return {
|
|
6180
6154
|
success: false,
|
|
6181
6155
|
message: "Failed to verify OTP"
|
|
6182
6156
|
};
|
|
6183
6157
|
}
|
|
6184
6158
|
},
|
|
6185
|
-
[setStoredEmail,
|
|
6159
|
+
[setStoredEmail, config?.routes?.defaultCallback, accounts, router]
|
|
6186
6160
|
);
|
|
6187
6161
|
const refreshToken = (0, import_react17.useCallback)(async () => {
|
|
6188
6162
|
try {
|
|
@@ -6258,9 +6232,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6258
6232
|
getSavedEmail: /* @__PURE__ */ __name(() => storedEmail, "getSavedEmail"),
|
|
6259
6233
|
saveEmail: setStoredEmail,
|
|
6260
6234
|
clearSavedEmail: clearStoredEmail,
|
|
6261
|
-
getSavedPhone: /* @__PURE__ */ __name(() => storedPhone, "getSavedPhone"),
|
|
6262
|
-
savePhone: setStoredPhone,
|
|
6263
|
-
clearSavedPhone: clearStoredPhone,
|
|
6264
6235
|
requestOTP,
|
|
6265
6236
|
verifyOTP,
|
|
6266
6237
|
refreshToken,
|
|
@@ -6283,9 +6254,6 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
6283
6254
|
storedEmail,
|
|
6284
6255
|
setStoredEmail,
|
|
6285
6256
|
clearStoredEmail,
|
|
6286
|
-
storedPhone,
|
|
6287
|
-
setStoredPhone,
|
|
6288
|
-
clearStoredPhone,
|
|
6289
6257
|
requestOTP,
|
|
6290
6258
|
verifyOTP,
|
|
6291
6259
|
refreshToken,
|
|
@@ -6319,12 +6287,6 @@ var defaultAuthState = {
|
|
|
6319
6287
|
}, "saveEmail"),
|
|
6320
6288
|
clearSavedEmail: /* @__PURE__ */ __name(() => {
|
|
6321
6289
|
}, "clearSavedEmail"),
|
|
6322
|
-
getSavedPhone: /* @__PURE__ */ __name(() => null, "getSavedPhone"),
|
|
6323
|
-
savePhone: /* @__PURE__ */ __name(() => {
|
|
6324
|
-
authLogger.warn("useAuth: savePhone called outside AuthProvider");
|
|
6325
|
-
}, "savePhone"),
|
|
6326
|
-
clearSavedPhone: /* @__PURE__ */ __name(() => {
|
|
6327
|
-
}, "clearSavedPhone"),
|
|
6328
6290
|
requestOTP: /* @__PURE__ */ __name(async () => {
|
|
6329
6291
|
authLogger.warn("useAuth: requestOTP called outside AuthProvider");
|
|
6330
6292
|
return { success: false, message: "AuthProvider not available" };
|