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