@djangocfg/api 2.1.455 → 2.1.457
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/dist/auth.cjs +1611 -2148
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +37 -82
- package/dist/auth.d.ts +37 -82
- package/dist/auth.mjs +1581 -2118
- package/dist/auth.mjs.map +1 -1
- package/dist/chunk-2G67QRNU.mjs +2096 -0
- package/dist/chunk-2G67QRNU.mjs.map +1 -0
- package/dist/chunk-32SRQGAC.mjs +2109 -0
- package/dist/chunk-32SRQGAC.mjs.map +1 -0
- package/dist/chunk-4BPRCONN.mjs +2098 -0
- package/dist/chunk-4BPRCONN.mjs.map +1 -0
- package/dist/chunk-5UZ2Z323.mjs +2021 -0
- package/dist/chunk-5UZ2Z323.mjs.map +1 -0
- package/dist/chunk-7KFTJXNM.mjs +2097 -0
- package/dist/chunk-7KFTJXNM.mjs.map +1 -0
- package/dist/chunk-BK4K5CVT.mjs +2019 -0
- package/dist/chunk-BK4K5CVT.mjs.map +1 -0
- package/dist/chunk-JLPJZ6WB.mjs +2020 -0
- package/dist/chunk-JLPJZ6WB.mjs.map +1 -0
- package/dist/chunk-TVU6PYJH.mjs +2032 -0
- package/dist/chunk-TVU6PYJH.mjs.map +1 -0
- package/dist/clients.cjs +292 -47
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.d.cts +23 -0
- package/dist/clients.d.ts +23 -0
- package/dist/clients.mjs +292 -47
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +200 -32
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +200 -32
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +292 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -4
- package/dist/index.d.ts +81 -4
- package/dist/index.mjs +292 -47
- package/dist/index.mjs.map +1 -1
- package/dist/sdk.gen-2SQOPTWJ.mjs +26 -0
- package/dist/sdk.gen-2SQOPTWJ.mjs.map +1 -0
- package/dist/sdk.gen-HSGK4C5S.mjs +26 -0
- package/dist/sdk.gen-HSGK4C5S.mjs.map +1 -0
- package/dist/sdk.gen-NUK2VGHO.mjs +25 -0
- package/dist/sdk.gen-NUK2VGHO.mjs.map +1 -0
- package/dist/sdk.gen-O4KAQUZB.mjs +26 -0
- package/dist/sdk.gen-O4KAQUZB.mjs.map +1 -0
- package/dist/sdk.gen-PPAVSBNT.mjs +26 -0
- package/dist/sdk.gen-PPAVSBNT.mjs.map +1 -0
- package/dist/sdk.gen-XLHLOCJ2.mjs +25 -0
- package/dist/sdk.gen-XLHLOCJ2.mjs.map +1 -0
- package/dist/sdk.gen-ZOE6NQAL.mjs +26 -0
- package/dist/sdk.gen-ZOE6NQAL.mjs.map +1 -0
- package/dist/sdk.gen-ZT7LGJVO.mjs +26 -0
- package/dist/sdk.gen-ZT7LGJVO.mjs.map +1 -0
- package/package.json +2 -2
- package/src/_api/generated/_cfg_accounts/hooks/index.ts +1 -0
- package/src/_api/generated/_cfg_accounts/hooks/useCfgAccountsTokenBlacklistCreate.ts +28 -0
- package/src/_api/generated/_cfg_accounts/openapi.json +99 -0
- package/src/_api/generated/_cfg_accounts/schemas/TokenBlacklistRequest.ts +11 -0
- package/src/_api/generated/_cfg_accounts/schemas/index.ts +1 -0
- package/src/_api/generated/_cfg_centrifugo/openapi.json +9 -0
- package/src/_api/generated/_cfg_totp/openapi.json +33 -0
- package/src/_api/generated/helpers/auth.ts +288 -31
- package/src/_api/generated/openapi.json +129 -0
- package/src/_api/generated/sdk.gen.ts +94 -16
- package/src/_api/generated/types.gen.ts +18 -0
- package/src/auth/__tests__/guard.test.ts +0 -31
- package/src/auth/constants.ts +6 -0
- package/src/auth/context/AccountsContext.tsx +14 -24
- package/src/auth/context/AuthContext.tsx +263 -607
- package/src/auth/context/types.ts +13 -0
- package/src/auth/hooks/index.ts +8 -5
- package/src/auth/hooks/useAuthRedirect.ts +17 -0
- package/src/auth/hooks/useGithubAuth.ts +3 -3
- package/src/auth/hooks/useSession.ts +22 -0
- package/src/auth/hooks/useTwoFactor.ts +8 -6
- package/src/auth/middlewares/index.ts +5 -6
- package/src/auth/utils/guard.ts +0 -22
- package/src/auth/utils/index.ts +0 -2
- package/src/_api/generated/_cfg_accounts/events.ts +0 -198
- package/src/_api/generated/_cfg_centrifugo/events.ts +0 -198
- package/src/_api/generated/_cfg_totp/events.ts +0 -198
- package/src/auth/__tests__/jwt.test.ts +0 -119
- package/src/auth/__tests__/sessionBootstrap.test.ts +0 -111
- package/src/auth/__tests__/useTokenRefresh.dom.test.tsx +0 -167
- package/src/auth/hooks/useAuthGuard.ts +0 -35
- package/src/auth/hooks/useTokenRefresh.ts +0 -131
- package/src/auth/refreshHandler.ts +0 -79
- package/src/auth/utils/jwt.ts +0 -66
package/dist/auth.cjs
CHANGED
|
@@ -41,19 +41,17 @@ __export(auth_exports, {
|
|
|
41
41
|
PatchedCfgUserUpdateRequestSchema: () => PatchedCfgUserUpdateRequestSchema,
|
|
42
42
|
authLogger: () => authLogger,
|
|
43
43
|
clearProfileCache: () => clearProfileCache,
|
|
44
|
+
consumeSavedRedirect: () => consumeSavedRedirect,
|
|
44
45
|
decodeBase64: () => decodeBase64,
|
|
45
46
|
encodeBase64: () => encodeBase64,
|
|
46
47
|
formatAuthError: () => formatAuthError,
|
|
47
48
|
getCacheMetadata: () => getCacheMetadata,
|
|
48
49
|
getCachedProfile: () => getCachedProfile,
|
|
49
|
-
getTokenExpiry: () => getTokenExpiry,
|
|
50
50
|
hasValidCache: () => hasValidCache,
|
|
51
51
|
isAllowedAuthPath: () => isAllowedAuthPath,
|
|
52
|
-
isTokenExpired: () => isTokenExpired,
|
|
53
|
-
isTokenExpiringSoon: () => isTokenExpiringSoon,
|
|
54
52
|
logger: () => logger,
|
|
55
|
-
nextAuthEvaluationDelay: () => nextAuthEvaluationDelay,
|
|
56
53
|
normalizePath: () => normalizePath,
|
|
54
|
+
peekSavedRedirect: () => peekSavedRedirect,
|
|
57
55
|
resolveGuardIsAuthenticated: () => resolveGuardIsAuthenticated,
|
|
58
56
|
resolveGuardIsLoading: () => resolveGuardIsLoading,
|
|
59
57
|
setCachedProfile: () => setCachedProfile,
|
|
@@ -62,7 +60,6 @@ __export(auth_exports, {
|
|
|
62
60
|
useAuth: () => useAuth,
|
|
63
61
|
useAuthForm: () => useAuthForm,
|
|
64
62
|
useAuthFormState: () => useAuthFormState,
|
|
65
|
-
useAuthGuard: () => useAuthGuard,
|
|
66
63
|
useAuthRedirectManager: () => useAuthRedirectManager,
|
|
67
64
|
useAuthValidation: () => useAuthValidation,
|
|
68
65
|
useAutoAuth: () => useAutoAuth,
|
|
@@ -72,8 +69,8 @@ __export(auth_exports, {
|
|
|
72
69
|
useGithubAuth: () => useGithubAuth,
|
|
73
70
|
useLocalStorage: () => useLocalStorage,
|
|
74
71
|
useQueryParams: () => useQueryParams,
|
|
72
|
+
useSession: () => useSession,
|
|
75
73
|
useSessionStorage: () => useSessionStorage,
|
|
76
|
-
useTokenRefresh: () => useTokenRefresh,
|
|
77
74
|
useTwoFactor: () => useTwoFactor,
|
|
78
75
|
useTwoFactorSetup: () => useTwoFactorSetup,
|
|
79
76
|
useTwoFactorStatus: () => useTwoFactorStatus,
|
|
@@ -92,896 +89,1068 @@ var AUTH_CONSTANTS = {
|
|
|
92
89
|
BACKUP_CODE_MAX_LENGTH: 12
|
|
93
90
|
};
|
|
94
91
|
|
|
95
|
-
// src/auth/utils/env.ts
|
|
96
|
-
var isDev = process.env.NODE_ENV === "development";
|
|
97
|
-
var isBrowser = typeof window !== "undefined";
|
|
98
|
-
var isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
99
|
-
var dpopEnabled = process.env.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
100
|
-
|
|
101
92
|
// src/auth/context/AuthContext.tsx
|
|
102
93
|
var import_navigation4 = require("next/navigation");
|
|
103
|
-
var
|
|
104
|
-
var import_swr5 = require("swr");
|
|
94
|
+
var import_react16 = require("react");
|
|
105
95
|
|
|
106
|
-
// src/
|
|
107
|
-
var
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
// src/_api/generated/helpers/errors.ts
|
|
97
|
+
var APIError = class extends Error {
|
|
98
|
+
constructor(statusCode, statusText, response, url, message) {
|
|
99
|
+
super(message || `HTTP ${statusCode}: ${statusText}`);
|
|
100
|
+
this.statusCode = statusCode;
|
|
101
|
+
this.statusText = statusText;
|
|
102
|
+
this.response = response;
|
|
103
|
+
this.url = url;
|
|
104
|
+
this.name = "APIError";
|
|
112
105
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
__name(getBasePath, "getBasePath");
|
|
116
|
-
function withBasePath(path, basePath) {
|
|
117
|
-
if (!basePath) {
|
|
118
|
-
return path;
|
|
106
|
+
static {
|
|
107
|
+
__name(this, "APIError");
|
|
119
108
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
get details() {
|
|
110
|
+
if (typeof this.response === "object" && this.response !== null) {
|
|
111
|
+
return this.response;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
get fieldErrors() {
|
|
116
|
+
const details = this.details;
|
|
117
|
+
if (!details) return null;
|
|
118
|
+
const fieldErrors = {};
|
|
119
|
+
for (const [key, value] of Object.entries(details)) {
|
|
120
|
+
if (Array.isArray(value)) fieldErrors[key] = value;
|
|
121
|
+
}
|
|
122
|
+
return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
|
|
123
|
+
}
|
|
124
|
+
get errorMessage() {
|
|
125
|
+
const details = this.details;
|
|
126
|
+
if (!details) return this.message;
|
|
127
|
+
if (details.detail) {
|
|
128
|
+
return Array.isArray(details.detail) ? details.detail.join(", ") : String(details.detail);
|
|
129
|
+
}
|
|
130
|
+
if (details.error) return String(details.error);
|
|
131
|
+
if (details.message) return String(details.message);
|
|
132
|
+
const fieldErrors = this.fieldErrors;
|
|
133
|
+
if (fieldErrors) {
|
|
134
|
+
const firstField = Object.keys(fieldErrors)[0];
|
|
135
|
+
if (firstField) return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
|
|
136
|
+
}
|
|
137
|
+
return this.message;
|
|
138
|
+
}
|
|
139
|
+
get isValidationError() {
|
|
140
|
+
return this.statusCode === 400;
|
|
141
|
+
}
|
|
142
|
+
get isAuthError() {
|
|
143
|
+
return this.statusCode === 401;
|
|
144
|
+
}
|
|
145
|
+
get isPermissionError() {
|
|
146
|
+
return this.statusCode === 403;
|
|
147
|
+
}
|
|
148
|
+
get isNotFoundError() {
|
|
149
|
+
return this.statusCode === 404;
|
|
150
|
+
}
|
|
151
|
+
get isServerError() {
|
|
152
|
+
return this.statusCode >= 500 && this.statusCode < 600;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/_api/generated/helpers/auth.ts
|
|
157
|
+
var ACCESS_KEY = "cfg.access_token";
|
|
158
|
+
var REFRESH_KEY = "cfg.refresh_token";
|
|
159
|
+
var API_KEY_KEY = "cfg.api_key";
|
|
160
|
+
var isBrowser = typeof window !== "undefined";
|
|
161
|
+
var localStorageBackend = {
|
|
162
|
+
get(key) {
|
|
163
|
+
if (!isBrowser) return null;
|
|
164
|
+
try {
|
|
165
|
+
return window.localStorage.getItem(key);
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
set(key, value) {
|
|
171
|
+
if (!isBrowser) return;
|
|
172
|
+
try {
|
|
173
|
+
if (value === null) window.localStorage.removeItem(key);
|
|
174
|
+
else window.localStorage.setItem(key, value);
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
180
|
+
var cookieBackend = {
|
|
181
|
+
get(key) {
|
|
182
|
+
if (!isBrowser) return null;
|
|
183
|
+
try {
|
|
184
|
+
const re = new RegExp(`(?:^|;\\s*)${encodeURIComponent(key)}=([^;]*)`);
|
|
185
|
+
const m = document.cookie.match(re);
|
|
186
|
+
return m ? decodeURIComponent(m[1]) : null;
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
set(key, value) {
|
|
192
|
+
if (!isBrowser) return;
|
|
193
|
+
try {
|
|
194
|
+
const k = encodeURIComponent(key);
|
|
195
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
196
|
+
if (value === null) {
|
|
197
|
+
document.cookie = `${k}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
|
|
198
|
+
} else {
|
|
199
|
+
const v = encodeURIComponent(value);
|
|
200
|
+
document.cookie = `${k}=${v}; Path=/; Max-Age=${COOKIE_MAX_AGE}; SameSite=Lax${secure}`;
|
|
201
|
+
}
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
var _storage = localStorageBackend;
|
|
207
|
+
var _storageMode = "localStorage";
|
|
208
|
+
function detectLocale() {
|
|
209
|
+
try {
|
|
210
|
+
if (typeof document !== "undefined") {
|
|
211
|
+
const m = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
|
|
212
|
+
if (m) return decodeURIComponent(m[1]);
|
|
213
|
+
}
|
|
214
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
215
|
+
return navigator.language;
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
123
220
|
}
|
|
124
|
-
__name(
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
router.push(href, options);
|
|
221
|
+
__name(detectLocale, "detectLocale");
|
|
222
|
+
function defaultBaseUrl() {
|
|
223
|
+
if (typeof window !== "undefined") {
|
|
224
|
+
try {
|
|
225
|
+
if (typeof process !== "undefined" && process.env) {
|
|
226
|
+
if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
|
|
227
|
+
if (process.env.NEXT_PUBLIC_API_PROXY_URL !== void 0)
|
|
228
|
+
return process.env.NEXT_PUBLIC_API_PROXY_URL;
|
|
229
|
+
return process.env.NEXT_PUBLIC_API_URL || "";
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
136
232
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
233
|
+
return "";
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
if (typeof process !== "undefined" && process.env) {
|
|
237
|
+
if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
|
|
238
|
+
return process.env.NEXT_PUBLIC_API_URL || "";
|
|
143
239
|
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
240
|
+
} catch {
|
|
241
|
+
}
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
__name(defaultBaseUrl, "defaultBaseUrl");
|
|
245
|
+
function defaultApiKey() {
|
|
246
|
+
try {
|
|
247
|
+
if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_KEY) {
|
|
248
|
+
return process.env.NEXT_PUBLIC_API_KEY;
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
__name(defaultApiKey, "defaultApiKey");
|
|
255
|
+
var _localeOverride = null;
|
|
256
|
+
var _apiKeyOverride = null;
|
|
257
|
+
var _baseUrlOverride = null;
|
|
258
|
+
var _withCredentials = true;
|
|
259
|
+
var _onUnauthorized = null;
|
|
260
|
+
var _refreshHandler = null;
|
|
261
|
+
var _refreshInflight = null;
|
|
262
|
+
var RETRY_MARKER = "X-Auth-Retry";
|
|
263
|
+
function jwtExpMs(token) {
|
|
264
|
+
try {
|
|
265
|
+
const payload = token.split(".")[1];
|
|
266
|
+
if (!payload) return null;
|
|
267
|
+
const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
268
|
+
return typeof json.exp === "number" ? json.exp * 1e3 : null;
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
__name(jwtExpMs, "jwtExpMs");
|
|
274
|
+
function computeSnapshot() {
|
|
275
|
+
const access = _storage.get(ACCESS_KEY);
|
|
276
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
277
|
+
const now = Date.now();
|
|
278
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
279
|
+
const accessAlive = access !== null && (accessExp === null || accessExp > now);
|
|
280
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
281
|
+
const refreshAlive = refresh !== null && (refreshExp === null || refreshExp > now);
|
|
163
282
|
return {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
hardPush,
|
|
167
|
-
hardReplace,
|
|
168
|
-
prefetch,
|
|
169
|
-
back,
|
|
170
|
-
forward,
|
|
171
|
-
refresh
|
|
283
|
+
status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
|
|
284
|
+
accessExpiresAt: accessExp
|
|
172
285
|
};
|
|
173
286
|
}
|
|
174
|
-
__name(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
var
|
|
178
|
-
var
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
287
|
+
__name(computeSnapshot, "computeSnapshot");
|
|
288
|
+
var SERVER_SNAPSHOT = { status: "anonymous", accessExpiresAt: null };
|
|
289
|
+
var SESSION_SYNC_EVENT = `cfg-auth:changed:${ACCESS_KEY}`;
|
|
290
|
+
var _snapshot = computeSnapshot();
|
|
291
|
+
var _sessionListeners = /* @__PURE__ */ new Set();
|
|
292
|
+
var _expiryTimer = null;
|
|
293
|
+
var _storageListenerInstalled = false;
|
|
294
|
+
function scheduleExpiryFlip() {
|
|
295
|
+
if (!isBrowser) return;
|
|
296
|
+
if (_expiryTimer !== null) {
|
|
297
|
+
clearTimeout(_expiryTimer);
|
|
298
|
+
_expiryTimer = null;
|
|
299
|
+
}
|
|
300
|
+
if (_sessionListeners.size === 0) return;
|
|
301
|
+
const now = Date.now();
|
|
302
|
+
const exps = [];
|
|
303
|
+
const access = _storage.get(ACCESS_KEY);
|
|
304
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
305
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
306
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
307
|
+
if (accessExp !== null && accessExp > now) exps.push(accessExp);
|
|
308
|
+
if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
|
|
309
|
+
if (!exps.length) return;
|
|
310
|
+
const delay = Math.min(Math.min(...exps) - now + 250, 2147483647);
|
|
311
|
+
_expiryTimer = setTimeout(notifySessionChanged, delay);
|
|
312
|
+
}
|
|
313
|
+
__name(scheduleExpiryFlip, "scheduleExpiryFlip");
|
|
314
|
+
function notifySessionChanged() {
|
|
315
|
+
const next = computeSnapshot();
|
|
316
|
+
const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
|
|
317
|
+
if (changed) _snapshot = next;
|
|
318
|
+
scheduleExpiryFlip();
|
|
319
|
+
if (!changed) return;
|
|
320
|
+
for (const listener of Array.from(_sessionListeners)) {
|
|
321
|
+
try {
|
|
322
|
+
listener();
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (isBrowser) {
|
|
327
|
+
try {
|
|
328
|
+
window.dispatchEvent(new Event(SESSION_SYNC_EVENT));
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
__name(notifySessionChanged, "notifySessionChanged");
|
|
334
|
+
function ensureStorageSync() {
|
|
335
|
+
if (!isBrowser || _storageListenerInstalled) return;
|
|
336
|
+
_storageListenerInstalled = true;
|
|
337
|
+
window.addEventListener("storage", (e) => {
|
|
338
|
+
if (e.key === null || e.key === ACCESS_KEY || e.key === REFRESH_KEY) {
|
|
339
|
+
notifySessionChanged();
|
|
184
340
|
}
|
|
185
|
-
return new URLSearchParams(window.location.search);
|
|
186
341
|
});
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
342
|
+
window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
|
|
343
|
+
}
|
|
344
|
+
__name(ensureStorageSync, "ensureStorageSync");
|
|
345
|
+
var _sessionExpiredHandlers = /* @__PURE__ */ new Set();
|
|
346
|
+
var _client = null;
|
|
347
|
+
function pushClientConfig() {
|
|
348
|
+
if (!_client) return;
|
|
349
|
+
_client.setConfig({
|
|
350
|
+
baseUrl: auth.getBaseUrl(),
|
|
351
|
+
credentials: _withCredentials ? "include" : "same-origin"
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
__name(pushClientConfig, "pushClientConfig");
|
|
355
|
+
var auth = {
|
|
356
|
+
// ── Storage mode ──────────────────────────────────────────────────
|
|
357
|
+
getStorageMode() {
|
|
358
|
+
return _storageMode;
|
|
359
|
+
},
|
|
360
|
+
setStorageMode(mode) {
|
|
361
|
+
_storageMode = mode;
|
|
362
|
+
_storage = mode === "cookie" ? cookieBackend : localStorageBackend;
|
|
363
|
+
notifySessionChanged();
|
|
364
|
+
},
|
|
365
|
+
// ── Bearer token ──────────────────────────────────────────────────
|
|
366
|
+
getToken() {
|
|
367
|
+
return _storage.get(ACCESS_KEY);
|
|
368
|
+
},
|
|
369
|
+
setToken(token) {
|
|
370
|
+
_storage.set(ACCESS_KEY, token);
|
|
371
|
+
notifySessionChanged();
|
|
372
|
+
},
|
|
373
|
+
getRefreshToken() {
|
|
374
|
+
return _storage.get(REFRESH_KEY);
|
|
375
|
+
},
|
|
376
|
+
setRefreshToken(token) {
|
|
377
|
+
_storage.set(REFRESH_KEY, token);
|
|
378
|
+
notifySessionChanged();
|
|
379
|
+
},
|
|
380
|
+
clearTokens() {
|
|
381
|
+
_storage.set(ACCESS_KEY, null);
|
|
382
|
+
_storage.set(REFRESH_KEY, null);
|
|
383
|
+
notifySessionChanged();
|
|
384
|
+
},
|
|
385
|
+
/** Session-aware: token PRESENT and not past its `exp` (or a live refresh
|
|
386
|
+
* token exists that can mint one). Prefer `getSnapshot().status` in React. */
|
|
387
|
+
isAuthenticated() {
|
|
388
|
+
return computeSnapshot().status === "authenticated";
|
|
389
|
+
},
|
|
390
|
+
// ── Session (the ONE write path for login/logout flows) ──────────
|
|
391
|
+
/**
|
|
392
|
+
* Persist a token pair atomically. Every login flow (OTP verify, 2FA,
|
|
393
|
+
* OAuth callback) and the refresh handler should end here — do NOT
|
|
394
|
+
* scatter `setToken`/`setRefreshToken` pairs through app code.
|
|
395
|
+
* `refresh: undefined` keeps the current refresh token (access-only
|
|
396
|
+
* rotation); `refresh: null` explicitly drops it.
|
|
397
|
+
*/
|
|
398
|
+
setSession(tokens) {
|
|
399
|
+
_storage.set(ACCESS_KEY, tokens.access);
|
|
400
|
+
if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
|
|
401
|
+
notifySessionChanged();
|
|
402
|
+
},
|
|
403
|
+
clearSession() {
|
|
404
|
+
auth.clearTokens();
|
|
405
|
+
},
|
|
406
|
+
// ── Reactive snapshot (for useSyncExternalStore) ──────────────────
|
|
407
|
+
/**
|
|
408
|
+
* @example React:
|
|
409
|
+
* const session = useSyncExternalStore(
|
|
410
|
+
* auth.subscribe, auth.getSnapshot, auth.getServerSnapshot,
|
|
411
|
+
* );
|
|
412
|
+
* const isAuthenticated = session.status === 'authenticated';
|
|
413
|
+
*/
|
|
414
|
+
getSnapshot() {
|
|
415
|
+
return _snapshot;
|
|
416
|
+
},
|
|
417
|
+
getServerSnapshot() {
|
|
418
|
+
return SERVER_SNAPSHOT;
|
|
419
|
+
},
|
|
420
|
+
subscribe(listener) {
|
|
421
|
+
ensureStorageSync();
|
|
422
|
+
_sessionListeners.add(listener);
|
|
423
|
+
notifySessionChanged();
|
|
424
|
+
return () => {
|
|
425
|
+
_sessionListeners.delete(listener);
|
|
426
|
+
if (_sessionListeners.size === 0 && _expiryTimer !== null) {
|
|
427
|
+
clearTimeout(_expiryTimer);
|
|
428
|
+
_expiryTimer = null;
|
|
429
|
+
}
|
|
210
430
|
};
|
|
431
|
+
},
|
|
432
|
+
/**
|
|
433
|
+
* Fired on TERMINAL 401 — after the refresh+retry path is exhausted.
|
|
434
|
+
* The store has already cleared the session before calling back, so
|
|
435
|
+
* handlers only need to route to login (no clear-then-redirect
|
|
436
|
+
* ordering to get wrong). Returns an unsubscribe function; multiple
|
|
437
|
+
* handlers compose (unlike the legacy single-slot `onUnauthorized`).
|
|
438
|
+
*/
|
|
439
|
+
onSessionExpired(cb) {
|
|
440
|
+
_sessionExpiredHandlers.add(cb);
|
|
211
441
|
return () => {
|
|
212
|
-
|
|
213
|
-
window.history.pushState = origPush;
|
|
214
|
-
window.history.replaceState = origReplace;
|
|
442
|
+
_sessionExpiredHandlers.delete(cb);
|
|
215
443
|
};
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
444
|
+
},
|
|
445
|
+
// ── API key ───────────────────────────────────────────────────────
|
|
446
|
+
getApiKey() {
|
|
447
|
+
return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
|
|
448
|
+
},
|
|
449
|
+
setApiKey(key) {
|
|
450
|
+
_apiKeyOverride = key;
|
|
451
|
+
},
|
|
452
|
+
setApiKeyPersist(key) {
|
|
453
|
+
_apiKeyOverride = key;
|
|
454
|
+
_storage.set(API_KEY_KEY, key);
|
|
455
|
+
},
|
|
456
|
+
clearApiKey() {
|
|
457
|
+
_apiKeyOverride = null;
|
|
458
|
+
_storage.set(API_KEY_KEY, null);
|
|
459
|
+
},
|
|
460
|
+
// ── Locale ────────────────────────────────────────────────────────
|
|
461
|
+
getLocale() {
|
|
462
|
+
return _localeOverride ?? detectLocale();
|
|
463
|
+
},
|
|
464
|
+
setLocale(locale) {
|
|
465
|
+
_localeOverride = locale;
|
|
466
|
+
},
|
|
467
|
+
// ── Base URL ──────────────────────────────────────────────────────
|
|
468
|
+
getBaseUrl() {
|
|
469
|
+
const url = _baseUrlOverride ?? defaultBaseUrl();
|
|
470
|
+
return url.replace(/\/$/, "");
|
|
471
|
+
},
|
|
472
|
+
setBaseUrl(url) {
|
|
473
|
+
_baseUrlOverride = url ? url.replace(/\/$/, "") : null;
|
|
474
|
+
pushClientConfig();
|
|
475
|
+
},
|
|
476
|
+
// ── Credentials toggle ────────────────────────────────────────────
|
|
477
|
+
getWithCredentials() {
|
|
478
|
+
return _withCredentials;
|
|
479
|
+
},
|
|
480
|
+
setWithCredentials(value) {
|
|
481
|
+
_withCredentials = value;
|
|
482
|
+
pushClientConfig();
|
|
483
|
+
},
|
|
484
|
+
// ── 401 handler ───────────────────────────────────────────────────
|
|
485
|
+
/**
|
|
486
|
+
* Fired when the server returns 401 AND no refresh path recovers it
|
|
487
|
+
* (no refresh token, no refresh handler, refresh failed, or retry
|
|
488
|
+
* still 401). The app should clear local state and redirect to login.
|
|
489
|
+
*
|
|
490
|
+
* NOT fired for 401 that gets transparently recovered by the refresh
|
|
491
|
+
* handler — those are invisible to callers.
|
|
492
|
+
*/
|
|
493
|
+
onUnauthorized(cb) {
|
|
494
|
+
_onUnauthorized = cb;
|
|
495
|
+
},
|
|
496
|
+
/**
|
|
497
|
+
* Register the refresh strategy. The handler receives the current
|
|
498
|
+
* refresh token and must call your refresh endpoint, returning
|
|
499
|
+
* `{ access, refresh? }` on success or `null` on failure.
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* auth.setRefreshHandler(async (refresh) => {
|
|
503
|
+
* const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
|
|
504
|
+
* return data ? { access: data.access, refresh: data.refresh } : null;
|
|
505
|
+
* });
|
|
506
|
+
*/
|
|
507
|
+
setRefreshHandler(fn) {
|
|
508
|
+
_refreshHandler = fn;
|
|
509
|
+
},
|
|
510
|
+
/**
|
|
511
|
+
* Proactively run the registered refresh handler right now, reusing the
|
|
512
|
+
* SAME single-flight promise as the 401-recovery interceptor. Callers
|
|
513
|
+
* (e.g. an expiry timer / focus / reconnect) get token rotation,
|
|
514
|
+
* de-duplication and rotated-token persistence for free — they must NOT
|
|
515
|
+
* re-implement any of it. Returns the fresh access token, or null if
|
|
516
|
+
* there is no handler / no refresh token / the refresh failed.
|
|
517
|
+
*/
|
|
518
|
+
refreshNow() {
|
|
519
|
+
return tryRefresh();
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
async function tryRefresh() {
|
|
523
|
+
if (_refreshInflight) return _refreshInflight;
|
|
524
|
+
if (!_refreshHandler) return null;
|
|
525
|
+
const runRefresh = /* @__PURE__ */ __name(async () => {
|
|
526
|
+
const refresh = auth.getRefreshToken();
|
|
527
|
+
if (!refresh) return null;
|
|
528
|
+
const result = await _refreshHandler(refresh);
|
|
529
|
+
if (!result?.access) return null;
|
|
530
|
+
auth.setToken(result.access);
|
|
531
|
+
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
532
|
+
return result.access;
|
|
533
|
+
}, "runRefresh");
|
|
534
|
+
_refreshInflight = (async () => {
|
|
535
|
+
try {
|
|
536
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
537
|
+
if (locks?.request) {
|
|
538
|
+
return await locks.request(`${REFRESH_KEY}:refresh`, runRefresh);
|
|
539
|
+
}
|
|
540
|
+
return await runRefresh();
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
} finally {
|
|
544
|
+
_refreshInflight = null;
|
|
545
|
+
}
|
|
546
|
+
})();
|
|
547
|
+
return _refreshInflight;
|
|
317
548
|
}
|
|
318
|
-
__name(
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
for (const
|
|
549
|
+
__name(tryRefresh, "tryRefresh");
|
|
550
|
+
function expireSession(response) {
|
|
551
|
+
auth.clearTokens();
|
|
552
|
+
for (const cb of Array.from(_sessionExpiredHandlers)) {
|
|
322
553
|
try {
|
|
323
|
-
|
|
554
|
+
cb(response);
|
|
555
|
+
} catch {
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (_onUnauthorized) {
|
|
559
|
+
try {
|
|
560
|
+
_onUnauthorized(response);
|
|
324
561
|
} catch {
|
|
325
562
|
}
|
|
326
563
|
}
|
|
327
564
|
}
|
|
328
|
-
__name(
|
|
329
|
-
function
|
|
330
|
-
_subscribers.add(fn);
|
|
565
|
+
__name(expireSession, "expireSession");
|
|
566
|
+
function dpopEnabled() {
|
|
331
567
|
try {
|
|
332
|
-
|
|
568
|
+
return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
333
569
|
} catch {
|
|
570
|
+
return false;
|
|
334
571
|
}
|
|
335
|
-
return () => _subscribers.delete(fn);
|
|
336
572
|
}
|
|
337
|
-
__name(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
573
|
+
__name(dpopEnabled, "dpopEnabled");
|
|
574
|
+
var _DPOP_DB = "cfg-auth";
|
|
575
|
+
var _DPOP_STORE = "keys";
|
|
576
|
+
var _DPOP_KEY_ID = "dpop-ec-p256";
|
|
577
|
+
function _idbOpen() {
|
|
578
|
+
return new Promise((resolve, reject) => {
|
|
579
|
+
const req = indexedDB.open(_DPOP_DB, 1);
|
|
580
|
+
req.onupgradeneeded = () => req.result.createObjectStore(_DPOP_STORE);
|
|
581
|
+
req.onsuccess = () => resolve(req.result);
|
|
582
|
+
req.onerror = () => reject(req.error);
|
|
583
|
+
});
|
|
345
584
|
}
|
|
346
|
-
__name(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
super(message || `HTTP ${statusCode}: ${statusText}`);
|
|
417
|
-
this.statusCode = statusCode;
|
|
418
|
-
this.statusText = statusText;
|
|
419
|
-
this.response = response;
|
|
420
|
-
this.url = url;
|
|
421
|
-
this.name = "APIError";
|
|
422
|
-
}
|
|
423
|
-
static {
|
|
424
|
-
__name(this, "APIError");
|
|
425
|
-
}
|
|
426
|
-
get details() {
|
|
427
|
-
if (typeof this.response === "object" && this.response !== null) {
|
|
428
|
-
return this.response;
|
|
429
|
-
}
|
|
585
|
+
__name(_idbOpen, "_idbOpen");
|
|
586
|
+
function _idbGet(key) {
|
|
587
|
+
return _idbOpen().then((db) => new Promise((resolve, reject) => {
|
|
588
|
+
const tx = db.transaction(_DPOP_STORE, "readonly");
|
|
589
|
+
const req = tx.objectStore(_DPOP_STORE).get(key);
|
|
590
|
+
req.onsuccess = () => resolve(req.result);
|
|
591
|
+
req.onerror = () => reject(req.error);
|
|
592
|
+
}));
|
|
593
|
+
}
|
|
594
|
+
__name(_idbGet, "_idbGet");
|
|
595
|
+
function _idbPut(key, value) {
|
|
596
|
+
return _idbOpen().then((db) => new Promise((resolve, reject) => {
|
|
597
|
+
const tx = db.transaction(_DPOP_STORE, "readwrite");
|
|
598
|
+
tx.objectStore(_DPOP_STORE).put(value, key);
|
|
599
|
+
tx.oncomplete = () => resolve();
|
|
600
|
+
tx.onerror = () => reject(tx.error);
|
|
601
|
+
}));
|
|
602
|
+
}
|
|
603
|
+
__name(_idbPut, "_idbPut");
|
|
604
|
+
var _dpopKeyPromise = null;
|
|
605
|
+
function _getDpopKeyPair() {
|
|
606
|
+
if (_dpopKeyPromise) return _dpopKeyPromise;
|
|
607
|
+
_dpopKeyPromise = (async () => {
|
|
608
|
+
const existing = await _idbGet(_DPOP_KEY_ID).catch(() => void 0);
|
|
609
|
+
if (existing) return existing;
|
|
610
|
+
const pair = await crypto.subtle.generateKey(
|
|
611
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
612
|
+
false,
|
|
613
|
+
// extractable:false — JS can sign but never export the private key
|
|
614
|
+
["sign"]
|
|
615
|
+
);
|
|
616
|
+
await _idbPut(_DPOP_KEY_ID, pair).catch(() => {
|
|
617
|
+
});
|
|
618
|
+
return pair;
|
|
619
|
+
})();
|
|
620
|
+
return _dpopKeyPromise;
|
|
621
|
+
}
|
|
622
|
+
__name(_getDpopKeyPair, "_getDpopKeyPair");
|
|
623
|
+
function _b64urlFromBytes(bytes) {
|
|
624
|
+
const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
625
|
+
let s = "";
|
|
626
|
+
for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i]);
|
|
627
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
628
|
+
}
|
|
629
|
+
__name(_b64urlFromBytes, "_b64urlFromBytes");
|
|
630
|
+
function _b64urlFromString(str) {
|
|
631
|
+
return _b64urlFromBytes(new TextEncoder().encode(str));
|
|
632
|
+
}
|
|
633
|
+
__name(_b64urlFromString, "_b64urlFromString");
|
|
634
|
+
async function _publicJwk(pub) {
|
|
635
|
+
const jwk = await crypto.subtle.exportKey("jwk", pub);
|
|
636
|
+
return { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y };
|
|
637
|
+
}
|
|
638
|
+
__name(_publicJwk, "_publicJwk");
|
|
639
|
+
async function _makeDpopProof(method, url) {
|
|
640
|
+
try {
|
|
641
|
+
const pair = await _getDpopKeyPair();
|
|
642
|
+
const jwk = await _publicJwk(pair.publicKey);
|
|
643
|
+
const header = { typ: "dpop+jwt", alg: "ES256", jwk };
|
|
644
|
+
const htu = url.split("#")[0].split("?")[0];
|
|
645
|
+
const jti = crypto.randomUUID && crypto.randomUUID() || _b64urlFromBytes(crypto.getRandomValues(new Uint8Array(16)));
|
|
646
|
+
const payload = { htm: method.toUpperCase(), htu, iat: Math.floor(Date.now() / 1e3), jti };
|
|
647
|
+
const signingInput = `${_b64urlFromString(JSON.stringify(header))}.${_b64urlFromString(JSON.stringify(payload))}`;
|
|
648
|
+
const sig = await crypto.subtle.sign(
|
|
649
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
650
|
+
pair.privateKey,
|
|
651
|
+
new TextEncoder().encode(signingInput)
|
|
652
|
+
);
|
|
653
|
+
return `${signingInput}.${_b64urlFromBytes(sig)}`;
|
|
654
|
+
} catch {
|
|
430
655
|
return null;
|
|
431
656
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
if (details.message) return String(details.message);
|
|
449
|
-
const fieldErrors = this.fieldErrors;
|
|
450
|
-
if (fieldErrors) {
|
|
451
|
-
const firstField = Object.keys(fieldErrors)[0];
|
|
452
|
-
if (firstField) return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
|
|
453
|
-
}
|
|
454
|
-
return this.message;
|
|
455
|
-
}
|
|
456
|
-
get isValidationError() {
|
|
457
|
-
return this.statusCode === 400;
|
|
458
|
-
}
|
|
459
|
-
get isAuthError() {
|
|
460
|
-
return this.statusCode === 401;
|
|
461
|
-
}
|
|
462
|
-
get isPermissionError() {
|
|
463
|
-
return this.statusCode === 403;
|
|
464
|
-
}
|
|
465
|
-
get isNotFoundError() {
|
|
466
|
-
return this.statusCode === 404;
|
|
467
|
-
}
|
|
468
|
-
get isServerError() {
|
|
469
|
-
return this.statusCode >= 500 && this.statusCode < 600;
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
// src/_api/generated/helpers/auth.ts
|
|
474
|
-
var ACCESS_KEY = "cfg.access_token";
|
|
475
|
-
var REFRESH_KEY = "cfg.refresh_token";
|
|
476
|
-
var API_KEY_KEY = "cfg.api_key";
|
|
477
|
-
var isBrowser2 = typeof window !== "undefined";
|
|
478
|
-
var localStorageBackend = {
|
|
479
|
-
get(key) {
|
|
480
|
-
if (!isBrowser2) return null;
|
|
481
|
-
try {
|
|
482
|
-
return window.localStorage.getItem(key);
|
|
483
|
-
} catch {
|
|
484
|
-
return null;
|
|
485
|
-
}
|
|
486
|
-
},
|
|
487
|
-
set(key, value) {
|
|
488
|
-
if (!isBrowser2) return;
|
|
657
|
+
}
|
|
658
|
+
__name(_makeDpopProof, "_makeDpopProof");
|
|
659
|
+
function installAuthOnClient(client2) {
|
|
660
|
+
if (_client) return;
|
|
661
|
+
_client = client2;
|
|
662
|
+
client2.setConfig({
|
|
663
|
+
baseUrl: auth.getBaseUrl(),
|
|
664
|
+
credentials: _withCredentials ? "include" : "same-origin"
|
|
665
|
+
});
|
|
666
|
+
client2.interceptors.request.use(async (request) => {
|
|
667
|
+
const token = auth.getToken();
|
|
668
|
+
if (token) request.headers.set("Authorization", `Bearer ${token}`);
|
|
669
|
+
const locale = auth.getLocale();
|
|
670
|
+
if (locale) request.headers.set("Accept-Language", locale);
|
|
671
|
+
const apiKey = auth.getApiKey();
|
|
672
|
+
if (apiKey && !request.headers.has("X-API-Key")) request.headers.set("X-API-Key", apiKey);
|
|
489
673
|
try {
|
|
490
|
-
|
|
491
|
-
|
|
674
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
675
|
+
if (tz) request.headers.set("X-Timezone", tz);
|
|
492
676
|
} catch {
|
|
493
677
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
get(key) {
|
|
499
|
-
if (!isBrowser2) return null;
|
|
500
|
-
try {
|
|
501
|
-
const re = new RegExp(`(?:^|;\\s*)${encodeURIComponent(key)}=([^;]*)`);
|
|
502
|
-
const m = document.cookie.match(re);
|
|
503
|
-
return m ? decodeURIComponent(m[1]) : null;
|
|
504
|
-
} catch {
|
|
505
|
-
return null;
|
|
678
|
+
request.headers.set("X-Client-Time", (/* @__PURE__ */ new Date()).toISOString());
|
|
679
|
+
if (dpopEnabled() && typeof window !== "undefined") {
|
|
680
|
+
const proof = await _makeDpopProof(request.method, request.url);
|
|
681
|
+
if (proof) request.headers.set("DPoP", proof);
|
|
506
682
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
683
|
+
return request;
|
|
684
|
+
});
|
|
685
|
+
client2.interceptors.error.use((err, res, req) => {
|
|
686
|
+
if (err instanceof APIError) return err;
|
|
687
|
+
const url = req?.url ?? "";
|
|
688
|
+
const status = res?.status ?? 0;
|
|
689
|
+
const statusText = res?.statusText ?? "";
|
|
690
|
+
return new APIError(status, statusText, err, url);
|
|
691
|
+
});
|
|
692
|
+
client2.interceptors.response.use(async (response, request) => {
|
|
693
|
+
if (response.status !== 401) return response;
|
|
694
|
+
if (request.headers.get(RETRY_MARKER)) {
|
|
695
|
+
expireSession(response);
|
|
696
|
+
return response;
|
|
697
|
+
}
|
|
698
|
+
const newToken = await tryRefresh();
|
|
699
|
+
if (!newToken) {
|
|
700
|
+
expireSession(response);
|
|
701
|
+
return response;
|
|
702
|
+
}
|
|
703
|
+
const retry = request.clone();
|
|
704
|
+
retry.headers.set("Authorization", `Bearer ${newToken}`);
|
|
705
|
+
retry.headers.set(RETRY_MARKER, "1");
|
|
706
|
+
if (dpopEnabled() && typeof window !== "undefined") {
|
|
707
|
+
const proof = await _makeDpopProof(retry.method, retry.url);
|
|
708
|
+
if (proof) retry.headers.set("DPoP", proof);
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
const retried = await fetch(retry);
|
|
712
|
+
if (retried.status === 401) expireSession(retried);
|
|
713
|
+
return retried;
|
|
519
714
|
} catch {
|
|
715
|
+
return response;
|
|
520
716
|
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
var
|
|
525
|
-
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
__name(installAuthOnClient, "installAuthOnClient");
|
|
720
|
+
var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
|
|
721
|
+
auth.setRefreshHandler(async (refresh) => {
|
|
526
722
|
try {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return navigator.language;
|
|
723
|
+
const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
|
|
724
|
+
const headers = { "Content-Type": "application/json" };
|
|
725
|
+
if (dpopEnabled() && typeof window !== "undefined") {
|
|
726
|
+
const proof = await _makeDpopProof("POST", url);
|
|
727
|
+
if (proof) headers["DPoP"] = proof;
|
|
533
728
|
}
|
|
729
|
+
const res = await fetch(url, {
|
|
730
|
+
method: "POST",
|
|
731
|
+
headers,
|
|
732
|
+
credentials: auth.getWithCredentials() ? "include" : "same-origin",
|
|
733
|
+
body: JSON.stringify({ refresh })
|
|
734
|
+
});
|
|
735
|
+
if (!res.ok) return null;
|
|
736
|
+
const data = await res.json();
|
|
737
|
+
return data?.access ? { access: data.access, refresh: data.refresh ?? refresh } : null;
|
|
534
738
|
} catch {
|
|
739
|
+
return null;
|
|
535
740
|
}
|
|
536
|
-
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// src/_api/generated/helpers/logger.ts
|
|
744
|
+
var import_consola = require("consola");
|
|
745
|
+
var DEFAULT_CONFIG = {
|
|
746
|
+
enabled: typeof process !== "undefined" && process.env.NODE_ENV !== "production",
|
|
747
|
+
logRequests: true,
|
|
748
|
+
logResponses: true,
|
|
749
|
+
logErrors: true,
|
|
750
|
+
logBodies: true,
|
|
751
|
+
logHeaders: false
|
|
752
|
+
};
|
|
753
|
+
var SENSITIVE_HEADERS = [
|
|
754
|
+
"authorization",
|
|
755
|
+
"cookie",
|
|
756
|
+
"set-cookie",
|
|
757
|
+
"x-api-key",
|
|
758
|
+
"x-csrf-token"
|
|
759
|
+
];
|
|
760
|
+
var APILogger = class {
|
|
761
|
+
static {
|
|
762
|
+
__name(this, "APILogger");
|
|
763
|
+
}
|
|
764
|
+
config;
|
|
765
|
+
consola;
|
|
766
|
+
constructor(config = {}) {
|
|
767
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
768
|
+
this.consola = config.consola || (0, import_consola.createConsola)({
|
|
769
|
+
level: this.config.enabled ? 4 : 0
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
enable() {
|
|
773
|
+
this.config.enabled = true;
|
|
774
|
+
}
|
|
775
|
+
disable() {
|
|
776
|
+
this.config.enabled = false;
|
|
777
|
+
}
|
|
778
|
+
setConfig(config) {
|
|
779
|
+
this.config = { ...this.config, ...config };
|
|
780
|
+
}
|
|
781
|
+
filterHeaders(headers) {
|
|
782
|
+
if (!headers) return {};
|
|
783
|
+
const filtered = {};
|
|
784
|
+
Object.keys(headers).forEach((key) => {
|
|
785
|
+
filtered[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "***" : headers[key] || "";
|
|
786
|
+
});
|
|
787
|
+
return filtered;
|
|
788
|
+
}
|
|
789
|
+
logRequest(request) {
|
|
790
|
+
if (!this.config.enabled || !this.config.logRequests) return;
|
|
791
|
+
const { method, url, headers, body } = request;
|
|
792
|
+
this.consola.start(`${method} ${url}`);
|
|
793
|
+
if (this.config.logHeaders && headers) this.consola.debug("Headers:", this.filterHeaders(headers));
|
|
794
|
+
if (this.config.logBodies && body) this.consola.debug("Body:", body);
|
|
795
|
+
}
|
|
796
|
+
logResponse(request, response) {
|
|
797
|
+
if (!this.config.enabled || !this.config.logResponses) return;
|
|
798
|
+
const { method, url } = request;
|
|
799
|
+
const { status, statusText, data, duration } = response;
|
|
800
|
+
this.consola.success(`${method} ${url} ${status} ${statusText} (${duration}ms)`);
|
|
801
|
+
if (this.config.logBodies && data) this.consola.debug("Response:", data);
|
|
802
|
+
}
|
|
803
|
+
logError(request, error) {
|
|
804
|
+
if (!this.config.enabled || !this.config.logErrors) return;
|
|
805
|
+
const { method, url } = request;
|
|
806
|
+
const { message, statusCode, fieldErrors, duration } = error;
|
|
807
|
+
this.consola.error(`${method} ${url} ${statusCode || "Network"} Error (${duration}ms)`);
|
|
808
|
+
this.consola.error("Message:", message);
|
|
809
|
+
if (fieldErrors && Object.keys(fieldErrors).length > 0) {
|
|
810
|
+
this.consola.error("Field Errors:");
|
|
811
|
+
Object.entries(fieldErrors).forEach(([field, errors]) => {
|
|
812
|
+
errors.forEach((err) => this.consola.error(` \u2022 ${field}: ${err}`));
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
info(message, ...args) {
|
|
817
|
+
if (this.config.enabled) this.consola.info(message, ...args);
|
|
818
|
+
}
|
|
819
|
+
warn(message, ...args) {
|
|
820
|
+
if (this.config.enabled) this.consola.warn(message, ...args);
|
|
821
|
+
}
|
|
822
|
+
error(message, ...args) {
|
|
823
|
+
if (this.config.enabled) this.consola.error(message, ...args);
|
|
824
|
+
}
|
|
825
|
+
debug(message, ...args) {
|
|
826
|
+
if (this.config.enabled) this.consola.debug(message, ...args);
|
|
827
|
+
}
|
|
828
|
+
success(message, ...args) {
|
|
829
|
+
if (this.config.enabled) this.consola.success(message, ...args);
|
|
830
|
+
}
|
|
831
|
+
withTag(tag) {
|
|
832
|
+
return this.consola.withTag(tag);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
var defaultLogger = new APILogger();
|
|
836
|
+
|
|
837
|
+
// src/auth/utils/env.ts
|
|
838
|
+
var isDev = process.env.NODE_ENV === "development";
|
|
839
|
+
var isBrowser2 = typeof window !== "undefined";
|
|
840
|
+
var isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
841
|
+
var dpopEnabled2 = process.env.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
842
|
+
|
|
843
|
+
// src/log-control.ts
|
|
844
|
+
var verboseByDefault = isDev || isStaticBuild;
|
|
845
|
+
var DEFAULT_LEVEL = verboseByDefault ? 4 : 1;
|
|
846
|
+
var _level = DEFAULT_LEVEL;
|
|
847
|
+
var _subscribers = /* @__PURE__ */ new Set();
|
|
848
|
+
function getLogLevel() {
|
|
849
|
+
return _level;
|
|
537
850
|
}
|
|
538
|
-
__name(
|
|
539
|
-
function
|
|
540
|
-
|
|
851
|
+
__name(getLogLevel, "getLogLevel");
|
|
852
|
+
function setLogLevel(level) {
|
|
853
|
+
_level = level;
|
|
854
|
+
for (const fn of _subscribers) {
|
|
541
855
|
try {
|
|
542
|
-
|
|
543
|
-
if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
|
|
544
|
-
if (process.env.NEXT_PUBLIC_API_PROXY_URL !== void 0)
|
|
545
|
-
return process.env.NEXT_PUBLIC_API_PROXY_URL;
|
|
546
|
-
return process.env.NEXT_PUBLIC_API_URL || "";
|
|
547
|
-
}
|
|
856
|
+
fn(level);
|
|
548
857
|
} catch {
|
|
549
858
|
}
|
|
550
|
-
return "";
|
|
551
859
|
}
|
|
860
|
+
}
|
|
861
|
+
__name(setLogLevel, "setLogLevel");
|
|
862
|
+
function onLogLevelChange(fn) {
|
|
863
|
+
_subscribers.add(fn);
|
|
552
864
|
try {
|
|
553
|
-
|
|
554
|
-
if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
|
|
555
|
-
return process.env.NEXT_PUBLIC_API_URL || "";
|
|
556
|
-
}
|
|
865
|
+
fn(_level);
|
|
557
866
|
} catch {
|
|
558
867
|
}
|
|
559
|
-
return
|
|
868
|
+
return () => _subscribers.delete(fn);
|
|
560
869
|
}
|
|
561
|
-
__name(
|
|
562
|
-
function
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
870
|
+
__name(onLogLevelChange, "onLogLevelChange");
|
|
871
|
+
function applyRoleLogPolicy(opts) {
|
|
872
|
+
const devMode = opts.isDev ?? verboseByDefault;
|
|
873
|
+
if (opts.isAdmin || devMode) {
|
|
874
|
+
setLogLevel(4);
|
|
875
|
+
} else {
|
|
876
|
+
setLogLevel(1);
|
|
568
877
|
}
|
|
569
|
-
return null;
|
|
570
878
|
}
|
|
571
|
-
__name(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
var
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
var _refreshHandler = null;
|
|
578
|
-
var _refreshInflight = null;
|
|
579
|
-
var RETRY_MARKER = "X-Auth-Retry";
|
|
580
|
-
var _client = null;
|
|
581
|
-
function pushClientConfig() {
|
|
582
|
-
if (!_client) return;
|
|
583
|
-
_client.setConfig({
|
|
584
|
-
baseUrl: auth.getBaseUrl(),
|
|
585
|
-
credentials: _withCredentials ? "include" : "same-origin"
|
|
586
|
-
});
|
|
879
|
+
__name(applyRoleLogPolicy, "applyRoleLogPolicy");
|
|
880
|
+
|
|
881
|
+
// src/auth/hooks/useSession.ts
|
|
882
|
+
var import_react = require("react");
|
|
883
|
+
function useSession() {
|
|
884
|
+
return (0, import_react.useSyncExternalStore)(auth.subscribe, auth.getSnapshot, auth.getServerSnapshot);
|
|
587
885
|
}
|
|
588
|
-
__name(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
_storage = mode === "cookie" ? cookieBackend : localStorageBackend;
|
|
597
|
-
},
|
|
598
|
-
// ── Bearer token ──────────────────────────────────────────────────
|
|
599
|
-
getToken() {
|
|
600
|
-
return _storage.get(ACCESS_KEY);
|
|
601
|
-
},
|
|
602
|
-
setToken(token) {
|
|
603
|
-
_storage.set(ACCESS_KEY, token);
|
|
604
|
-
},
|
|
605
|
-
getRefreshToken() {
|
|
606
|
-
return _storage.get(REFRESH_KEY);
|
|
607
|
-
},
|
|
608
|
-
setRefreshToken(token) {
|
|
609
|
-
_storage.set(REFRESH_KEY, token);
|
|
610
|
-
},
|
|
611
|
-
clearTokens() {
|
|
612
|
-
_storage.set(ACCESS_KEY, null);
|
|
613
|
-
_storage.set(REFRESH_KEY, null);
|
|
614
|
-
},
|
|
615
|
-
isAuthenticated() {
|
|
616
|
-
return _storage.get(ACCESS_KEY) !== null;
|
|
617
|
-
},
|
|
618
|
-
// ── API key ───────────────────────────────────────────────────────
|
|
619
|
-
getApiKey() {
|
|
620
|
-
return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
|
|
621
|
-
},
|
|
622
|
-
setApiKey(key) {
|
|
623
|
-
_apiKeyOverride = key;
|
|
624
|
-
},
|
|
625
|
-
setApiKeyPersist(key) {
|
|
626
|
-
_apiKeyOverride = key;
|
|
627
|
-
_storage.set(API_KEY_KEY, key);
|
|
628
|
-
},
|
|
629
|
-
clearApiKey() {
|
|
630
|
-
_apiKeyOverride = null;
|
|
631
|
-
_storage.set(API_KEY_KEY, null);
|
|
632
|
-
},
|
|
633
|
-
// ── Locale ────────────────────────────────────────────────────────
|
|
634
|
-
getLocale() {
|
|
635
|
-
return _localeOverride ?? detectLocale();
|
|
636
|
-
},
|
|
637
|
-
setLocale(locale) {
|
|
638
|
-
_localeOverride = locale;
|
|
639
|
-
},
|
|
640
|
-
// ── Base URL ──────────────────────────────────────────────────────
|
|
641
|
-
getBaseUrl() {
|
|
642
|
-
const url = _baseUrlOverride ?? defaultBaseUrl();
|
|
643
|
-
return url.replace(/\/$/, "");
|
|
644
|
-
},
|
|
645
|
-
setBaseUrl(url) {
|
|
646
|
-
_baseUrlOverride = url ? url.replace(/\/$/, "") : null;
|
|
647
|
-
pushClientConfig();
|
|
648
|
-
},
|
|
649
|
-
// ── Credentials toggle ────────────────────────────────────────────
|
|
650
|
-
getWithCredentials() {
|
|
651
|
-
return _withCredentials;
|
|
652
|
-
},
|
|
653
|
-
setWithCredentials(value) {
|
|
654
|
-
_withCredentials = value;
|
|
655
|
-
pushClientConfig();
|
|
656
|
-
},
|
|
657
|
-
// ── 401 handler ───────────────────────────────────────────────────
|
|
658
|
-
/**
|
|
659
|
-
* Fired when the server returns 401 AND no refresh path recovers it
|
|
660
|
-
* (no refresh token, no refresh handler, refresh failed, or retry
|
|
661
|
-
* still 401). The app should clear local state and redirect to login.
|
|
662
|
-
*
|
|
663
|
-
* NOT fired for 401 that gets transparently recovered by the refresh
|
|
664
|
-
* handler — those are invisible to callers.
|
|
665
|
-
*/
|
|
666
|
-
onUnauthorized(cb) {
|
|
667
|
-
_onUnauthorized = cb;
|
|
668
|
-
},
|
|
669
|
-
/**
|
|
670
|
-
* Register the refresh strategy. The handler receives the current
|
|
671
|
-
* refresh token and must call your refresh endpoint, returning
|
|
672
|
-
* `{ access, refresh? }` on success or `null` on failure.
|
|
673
|
-
*
|
|
674
|
-
* @example
|
|
675
|
-
* auth.setRefreshHandler(async (refresh) => {
|
|
676
|
-
* const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
|
|
677
|
-
* return data ? { access: data.access, refresh: data.refresh } : null;
|
|
678
|
-
* });
|
|
679
|
-
*/
|
|
680
|
-
setRefreshHandler(fn) {
|
|
681
|
-
_refreshHandler = fn;
|
|
682
|
-
},
|
|
683
|
-
/**
|
|
684
|
-
* Proactively run the registered refresh handler right now, reusing the
|
|
685
|
-
* SAME single-flight promise as the 401-recovery interceptor. Callers
|
|
686
|
-
* (e.g. an expiry timer / focus / reconnect) get token rotation,
|
|
687
|
-
* de-duplication and rotated-token persistence for free — they must NOT
|
|
688
|
-
* re-implement any of it. Returns the fresh access token, or null if
|
|
689
|
-
* there is no handler / no refresh token / the refresh failed.
|
|
690
|
-
*/
|
|
691
|
-
refreshNow() {
|
|
692
|
-
return tryRefresh();
|
|
693
|
-
}
|
|
694
|
-
};
|
|
695
|
-
async function tryRefresh() {
|
|
696
|
-
if (_refreshInflight) return _refreshInflight;
|
|
697
|
-
if (!_refreshHandler) return null;
|
|
698
|
-
const refresh = auth.getRefreshToken();
|
|
699
|
-
if (!refresh) return null;
|
|
700
|
-
_refreshInflight = (async () => {
|
|
701
|
-
try {
|
|
702
|
-
const result = await _refreshHandler(refresh);
|
|
703
|
-
if (!result?.access) return null;
|
|
704
|
-
auth.setToken(result.access);
|
|
705
|
-
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
706
|
-
return result.access;
|
|
707
|
-
} catch {
|
|
708
|
-
return null;
|
|
709
|
-
} finally {
|
|
710
|
-
_refreshInflight = null;
|
|
711
|
-
}
|
|
712
|
-
})();
|
|
713
|
-
return _refreshInflight;
|
|
714
|
-
}
|
|
715
|
-
__name(tryRefresh, "tryRefresh");
|
|
716
|
-
function dpopEnabled2() {
|
|
717
|
-
try {
|
|
718
|
-
return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
719
|
-
} catch {
|
|
720
|
-
return false;
|
|
886
|
+
__name(useSession, "useSession");
|
|
887
|
+
|
|
888
|
+
// src/auth/hooks/useCfgRouter.ts
|
|
889
|
+
var import_navigation = require("next/navigation");
|
|
890
|
+
var import_react2 = require("react");
|
|
891
|
+
function getBasePath() {
|
|
892
|
+
if (typeof process === "undefined") {
|
|
893
|
+
return "";
|
|
721
894
|
}
|
|
895
|
+
return process.env.NEXT_PUBLIC_BASE_PATH || "";
|
|
722
896
|
}
|
|
723
|
-
__name(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
function _idbOpen() {
|
|
728
|
-
return new Promise((resolve, reject) => {
|
|
729
|
-
const req = indexedDB.open(_DPOP_DB, 1);
|
|
730
|
-
req.onupgradeneeded = () => req.result.createObjectStore(_DPOP_STORE);
|
|
731
|
-
req.onsuccess = () => resolve(req.result);
|
|
732
|
-
req.onerror = () => reject(req.error);
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
__name(_idbOpen, "_idbOpen");
|
|
736
|
-
function _idbGet(key) {
|
|
737
|
-
return _idbOpen().then((db) => new Promise((resolve, reject) => {
|
|
738
|
-
const tx = db.transaction(_DPOP_STORE, "readonly");
|
|
739
|
-
const req = tx.objectStore(_DPOP_STORE).get(key);
|
|
740
|
-
req.onsuccess = () => resolve(req.result);
|
|
741
|
-
req.onerror = () => reject(req.error);
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
744
|
-
__name(_idbGet, "_idbGet");
|
|
745
|
-
function _idbPut(key, value) {
|
|
746
|
-
return _idbOpen().then((db) => new Promise((resolve, reject) => {
|
|
747
|
-
const tx = db.transaction(_DPOP_STORE, "readwrite");
|
|
748
|
-
tx.objectStore(_DPOP_STORE).put(value, key);
|
|
749
|
-
tx.oncomplete = () => resolve();
|
|
750
|
-
tx.onerror = () => reject(tx.error);
|
|
751
|
-
}));
|
|
752
|
-
}
|
|
753
|
-
__name(_idbPut, "_idbPut");
|
|
754
|
-
var _dpopKeyPromise = null;
|
|
755
|
-
function _getDpopKeyPair() {
|
|
756
|
-
if (_dpopKeyPromise) return _dpopKeyPromise;
|
|
757
|
-
_dpopKeyPromise = (async () => {
|
|
758
|
-
const existing = await _idbGet(_DPOP_KEY_ID).catch(() => void 0);
|
|
759
|
-
if (existing) return existing;
|
|
760
|
-
const pair = await crypto.subtle.generateKey(
|
|
761
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
762
|
-
false,
|
|
763
|
-
// extractable:false — JS can sign but never export the private key
|
|
764
|
-
["sign"]
|
|
765
|
-
);
|
|
766
|
-
await _idbPut(_DPOP_KEY_ID, pair).catch(() => {
|
|
767
|
-
});
|
|
768
|
-
return pair;
|
|
769
|
-
})();
|
|
770
|
-
return _dpopKeyPromise;
|
|
771
|
-
}
|
|
772
|
-
__name(_getDpopKeyPair, "_getDpopKeyPair");
|
|
773
|
-
function _b64urlFromBytes(bytes) {
|
|
774
|
-
const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
775
|
-
let s = "";
|
|
776
|
-
for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i]);
|
|
777
|
-
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
778
|
-
}
|
|
779
|
-
__name(_b64urlFromBytes, "_b64urlFromBytes");
|
|
780
|
-
function _b64urlFromString(str) {
|
|
781
|
-
return _b64urlFromBytes(new TextEncoder().encode(str));
|
|
782
|
-
}
|
|
783
|
-
__name(_b64urlFromString, "_b64urlFromString");
|
|
784
|
-
async function _publicJwk(pub) {
|
|
785
|
-
const jwk = await crypto.subtle.exportKey("jwk", pub);
|
|
786
|
-
return { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y };
|
|
787
|
-
}
|
|
788
|
-
__name(_publicJwk, "_publicJwk");
|
|
789
|
-
async function _makeDpopProof(method, url) {
|
|
790
|
-
try {
|
|
791
|
-
const pair = await _getDpopKeyPair();
|
|
792
|
-
const jwk = await _publicJwk(pair.publicKey);
|
|
793
|
-
const header = { typ: "dpop+jwt", alg: "ES256", jwk };
|
|
794
|
-
const htu = url.split("#")[0].split("?")[0];
|
|
795
|
-
const jti = crypto.randomUUID && crypto.randomUUID() || _b64urlFromBytes(crypto.getRandomValues(new Uint8Array(16)));
|
|
796
|
-
const payload = { htm: method.toUpperCase(), htu, iat: Math.floor(Date.now() / 1e3), jti };
|
|
797
|
-
const signingInput = `${_b64urlFromString(JSON.stringify(header))}.${_b64urlFromString(JSON.stringify(payload))}`;
|
|
798
|
-
const sig = await crypto.subtle.sign(
|
|
799
|
-
{ name: "ECDSA", hash: "SHA-256" },
|
|
800
|
-
pair.privateKey,
|
|
801
|
-
new TextEncoder().encode(signingInput)
|
|
802
|
-
);
|
|
803
|
-
return `${signingInput}.${_b64urlFromBytes(sig)}`;
|
|
804
|
-
} catch {
|
|
805
|
-
return null;
|
|
897
|
+
__name(getBasePath, "getBasePath");
|
|
898
|
+
function withBasePath(path, basePath) {
|
|
899
|
+
if (!basePath) {
|
|
900
|
+
return path;
|
|
806
901
|
}
|
|
902
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
903
|
+
const normalizedBasePath = basePath.replace(/\/$/, "");
|
|
904
|
+
return `${normalizedBasePath}${normalizedPath}`;
|
|
807
905
|
}
|
|
808
|
-
__name(
|
|
809
|
-
function
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
if (locale) request.headers.set("Accept-Language", locale);
|
|
821
|
-
const apiKey = auth.getApiKey();
|
|
822
|
-
if (apiKey && !request.headers.has("X-API-Key")) request.headers.set("X-API-Key", apiKey);
|
|
823
|
-
try {
|
|
824
|
-
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
825
|
-
if (tz) request.headers.set("X-Timezone", tz);
|
|
826
|
-
} catch {
|
|
906
|
+
__name(withBasePath, "withBasePath");
|
|
907
|
+
function useCfgRouter() {
|
|
908
|
+
const router = (0, import_navigation.useRouter)();
|
|
909
|
+
const basePath = (0, import_react2.useMemo)(() => getBasePath(), []);
|
|
910
|
+
const isStaticBuild2 = (0, import_react2.useMemo)(() => {
|
|
911
|
+
return typeof process !== "undefined" && process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
|
|
912
|
+
}, []);
|
|
913
|
+
const push = (0, import_react2.useCallback)((href, options) => {
|
|
914
|
+
if (basePath) {
|
|
915
|
+
window.location.href = withBasePath(href, basePath);
|
|
916
|
+
} else {
|
|
917
|
+
router.push(href, options);
|
|
827
918
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
919
|
+
}, [router, basePath]);
|
|
920
|
+
const replace = (0, import_react2.useCallback)((href, options) => {
|
|
921
|
+
if (basePath) {
|
|
922
|
+
window.location.replace(withBasePath(href, basePath));
|
|
923
|
+
} else {
|
|
924
|
+
router.replace(href, options);
|
|
832
925
|
}
|
|
833
|
-
|
|
926
|
+
}, [router, basePath]);
|
|
927
|
+
const hardPush = (0, import_react2.useCallback)((href) => {
|
|
928
|
+
window.location.href = withBasePath(href, basePath);
|
|
929
|
+
}, [basePath]);
|
|
930
|
+
const hardReplace = (0, import_react2.useCallback)((href) => {
|
|
931
|
+
window.location.replace(withBasePath(href, basePath));
|
|
932
|
+
}, [basePath]);
|
|
933
|
+
const prefetch = (0, import_react2.useCallback)((href) => {
|
|
934
|
+
router.prefetch(href);
|
|
935
|
+
}, [router]);
|
|
936
|
+
const back = (0, import_react2.useCallback)(() => {
|
|
937
|
+
router.back();
|
|
938
|
+
}, [router]);
|
|
939
|
+
const forward = (0, import_react2.useCallback)(() => {
|
|
940
|
+
router.forward();
|
|
941
|
+
}, [router]);
|
|
942
|
+
const refresh = (0, import_react2.useCallback)(() => {
|
|
943
|
+
router.refresh();
|
|
944
|
+
}, [router]);
|
|
945
|
+
return {
|
|
946
|
+
push,
|
|
947
|
+
replace,
|
|
948
|
+
hardPush,
|
|
949
|
+
hardReplace,
|
|
950
|
+
prefetch,
|
|
951
|
+
back,
|
|
952
|
+
forward,
|
|
953
|
+
refresh
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
__name(useCfgRouter, "useCfgRouter");
|
|
957
|
+
|
|
958
|
+
// src/auth/hooks/useQueryParams.ts
|
|
959
|
+
var import_navigation2 = require("next/navigation");
|
|
960
|
+
var import_react3 = require("react");
|
|
961
|
+
function useQueryParams() {
|
|
962
|
+
const pathname = (0, import_navigation2.usePathname)();
|
|
963
|
+
const [queryParams, setQueryParams] = (0, import_react3.useState)(() => {
|
|
964
|
+
if (typeof window === "undefined") {
|
|
965
|
+
return new URLSearchParams();
|
|
966
|
+
}
|
|
967
|
+
return new URLSearchParams(window.location.search);
|
|
834
968
|
});
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (response.status !== 401) return response;
|
|
844
|
-
if (request.headers.get(RETRY_MARKER)) {
|
|
845
|
-
if (_onUnauthorized) {
|
|
846
|
-
try {
|
|
847
|
-
_onUnauthorized(response);
|
|
848
|
-
} catch {
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
return response;
|
|
852
|
-
}
|
|
853
|
-
const newToken = await tryRefresh();
|
|
854
|
-
if (!newToken) {
|
|
855
|
-
if (_onUnauthorized) {
|
|
856
|
-
try {
|
|
857
|
-
_onUnauthorized(response);
|
|
858
|
-
} catch {
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
return response;
|
|
862
|
-
}
|
|
863
|
-
const retry = request.clone();
|
|
864
|
-
retry.headers.set("Authorization", `Bearer ${newToken}`);
|
|
865
|
-
retry.headers.set(RETRY_MARKER, "1");
|
|
866
|
-
if (dpopEnabled2() && typeof window !== "undefined") {
|
|
867
|
-
const proof = await _makeDpopProof(retry.method, retry.url);
|
|
868
|
-
if (proof) retry.headers.set("DPoP", proof);
|
|
869
|
-
}
|
|
870
|
-
try {
|
|
871
|
-
const retried = await fetch(retry);
|
|
872
|
-
if (retried.status === 401 && _onUnauthorized) {
|
|
873
|
-
try {
|
|
874
|
-
_onUnauthorized(retried);
|
|
875
|
-
} catch {
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return retried;
|
|
879
|
-
} catch {
|
|
880
|
-
if (_onUnauthorized) {
|
|
881
|
-
try {
|
|
882
|
-
_onUnauthorized(response);
|
|
883
|
-
} catch {
|
|
884
|
-
}
|
|
969
|
+
const lastSearchRef = (0, import_react3.useRef)("");
|
|
970
|
+
(0, import_react3.useEffect)(() => {
|
|
971
|
+
if (typeof window === "undefined") return;
|
|
972
|
+
const updateQueryParams = /* @__PURE__ */ __name(() => {
|
|
973
|
+
const currentSearch = window.location.search;
|
|
974
|
+
if (currentSearch !== lastSearchRef.current) {
|
|
975
|
+
lastSearchRef.current = currentSearch;
|
|
976
|
+
setQueryParams(new URLSearchParams(currentSearch));
|
|
885
977
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
978
|
+
}, "updateQueryParams");
|
|
979
|
+
updateQueryParams();
|
|
980
|
+
window.addEventListener("popstate", updateQueryParams);
|
|
981
|
+
const origPush = window.history.pushState;
|
|
982
|
+
const origReplace = window.history.replaceState;
|
|
983
|
+
window.history.pushState = function(...args) {
|
|
984
|
+
const result = origPush.apply(this, args);
|
|
985
|
+
updateQueryParams();
|
|
986
|
+
return result;
|
|
987
|
+
};
|
|
988
|
+
window.history.replaceState = function(...args) {
|
|
989
|
+
const result = origReplace.apply(this, args);
|
|
990
|
+
updateQueryParams();
|
|
991
|
+
return result;
|
|
992
|
+
};
|
|
993
|
+
return () => {
|
|
994
|
+
window.removeEventListener("popstate", updateQueryParams);
|
|
995
|
+
window.history.pushState = origPush;
|
|
996
|
+
window.history.replaceState = origReplace;
|
|
997
|
+
};
|
|
998
|
+
}, [pathname]);
|
|
999
|
+
return queryParams;
|
|
889
1000
|
}
|
|
890
|
-
__name(
|
|
1001
|
+
__name(useQueryParams, "useQueryParams");
|
|
891
1002
|
|
|
892
|
-
// src/
|
|
893
|
-
var
|
|
894
|
-
var
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
"
|
|
905
|
-
"
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
];
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
disable() {
|
|
925
|
-
this.config.enabled = false;
|
|
926
|
-
}
|
|
927
|
-
setConfig(config) {
|
|
928
|
-
this.config = { ...this.config, ...config };
|
|
929
|
-
}
|
|
930
|
-
filterHeaders(headers) {
|
|
931
|
-
if (!headers) return {};
|
|
932
|
-
const filtered = {};
|
|
933
|
-
Object.keys(headers).forEach((key) => {
|
|
934
|
-
filtered[key] = SENSITIVE_HEADERS.includes(key.toLowerCase()) ? "***" : headers[key] || "";
|
|
935
|
-
});
|
|
936
|
-
return filtered;
|
|
937
|
-
}
|
|
938
|
-
logRequest(request) {
|
|
939
|
-
if (!this.config.enabled || !this.config.logRequests) return;
|
|
940
|
-
const { method, url, headers, body } = request;
|
|
941
|
-
this.consola.start(`${method} ${url}`);
|
|
942
|
-
if (this.config.logHeaders && headers) this.consola.debug("Headers:", this.filterHeaders(headers));
|
|
943
|
-
if (this.config.logBodies && body) this.consola.debug("Body:", body);
|
|
944
|
-
}
|
|
945
|
-
logResponse(request, response) {
|
|
946
|
-
if (!this.config.enabled || !this.config.logResponses) return;
|
|
947
|
-
const { method, url } = request;
|
|
948
|
-
const { status, statusText, data, duration } = response;
|
|
949
|
-
this.consola.success(`${method} ${url} ${status} ${statusText} (${duration}ms)`);
|
|
950
|
-
if (this.config.logBodies && data) this.consola.debug("Response:", data);
|
|
951
|
-
}
|
|
952
|
-
logError(request, error) {
|
|
953
|
-
if (!this.config.enabled || !this.config.logErrors) return;
|
|
954
|
-
const { method, url } = request;
|
|
955
|
-
const { message, statusCode, fieldErrors, duration } = error;
|
|
956
|
-
this.consola.error(`${method} ${url} ${statusCode || "Network"} Error (${duration}ms)`);
|
|
957
|
-
this.consola.error("Message:", message);
|
|
958
|
-
if (fieldErrors && Object.keys(fieldErrors).length > 0) {
|
|
959
|
-
this.consola.error("Field Errors:");
|
|
960
|
-
Object.entries(fieldErrors).forEach(([field, errors]) => {
|
|
961
|
-
errors.forEach((err) => this.consola.error(` \u2022 ${field}: ${err}`));
|
|
1003
|
+
// src/auth/hooks/useAuthFormState.ts
|
|
1004
|
+
var import_react4 = require("react");
|
|
1005
|
+
var formatCountdown = /* @__PURE__ */ __name((s) => {
|
|
1006
|
+
if (s <= 0) return "";
|
|
1007
|
+
const m = Math.floor(s / 60);
|
|
1008
|
+
return m > 0 ? `${m}:${String(s % 60).padStart(2, "0")}` : `${s}s`;
|
|
1009
|
+
}, "formatCountdown");
|
|
1010
|
+
var useAuthFormState = /* @__PURE__ */ __name((initialIdentifier = "") => {
|
|
1011
|
+
const [identifier, setIdentifier] = (0, import_react4.useState)(initialIdentifier);
|
|
1012
|
+
const [otp, setOtp] = (0, import_react4.useState)("");
|
|
1013
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
1014
|
+
const [acceptedTerms, setAcceptedTerms] = (0, import_react4.useState)(true);
|
|
1015
|
+
const [step, setStep] = (0, import_react4.useState)("identifier");
|
|
1016
|
+
const [error, setError] = (0, import_react4.useState)("");
|
|
1017
|
+
const [webmail, setWebmail] = (0, import_react4.useState)(null);
|
|
1018
|
+
const [twoFactorSessionId, setTwoFactorSessionId] = (0, import_react4.useState)(null);
|
|
1019
|
+
const [shouldPrompt2FA, setShouldPrompt2FA] = (0, import_react4.useState)(false);
|
|
1020
|
+
const [twoFactorCode, setTwoFactorCode] = (0, import_react4.useState)("");
|
|
1021
|
+
const [useBackupCode, setUseBackupCode] = (0, import_react4.useState)(false);
|
|
1022
|
+
const [rateLimitSeconds, setRateLimitSeconds] = (0, import_react4.useState)(0);
|
|
1023
|
+
const rateLimitTimerRef = (0, import_react4.useRef)(null);
|
|
1024
|
+
const startRateLimitCountdown = (0, import_react4.useCallback)((seconds) => {
|
|
1025
|
+
if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
|
|
1026
|
+
setRateLimitSeconds(seconds);
|
|
1027
|
+
rateLimitTimerRef.current = setInterval(() => {
|
|
1028
|
+
setRateLimitSeconds((prev) => {
|
|
1029
|
+
if (prev <= 1) {
|
|
1030
|
+
clearInterval(rateLimitTimerRef.current);
|
|
1031
|
+
rateLimitTimerRef.current = null;
|
|
1032
|
+
return 0;
|
|
1033
|
+
}
|
|
1034
|
+
return prev - 1;
|
|
962
1035
|
});
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
if (
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1036
|
+
}, 1e3);
|
|
1037
|
+
}, []);
|
|
1038
|
+
(0, import_react4.useEffect)(() => () => {
|
|
1039
|
+
if (rateLimitTimerRef.current) clearInterval(rateLimitTimerRef.current);
|
|
1040
|
+
}, []);
|
|
1041
|
+
const clearError = (0, import_react4.useCallback)(() => setError(""), []);
|
|
1042
|
+
return {
|
|
1043
|
+
// State
|
|
1044
|
+
identifier,
|
|
1045
|
+
otp,
|
|
1046
|
+
isLoading,
|
|
1047
|
+
acceptedTerms,
|
|
1048
|
+
step,
|
|
1049
|
+
error,
|
|
1050
|
+
twoFactorSessionId,
|
|
1051
|
+
shouldPrompt2FA,
|
|
1052
|
+
twoFactorCode,
|
|
1053
|
+
useBackupCode,
|
|
1054
|
+
rateLimitSeconds,
|
|
1055
|
+
isRateLimited: rateLimitSeconds > 0,
|
|
1056
|
+
rateLimitLabel: formatCountdown(rateLimitSeconds),
|
|
1057
|
+
webmail,
|
|
1058
|
+
// Handlers
|
|
1059
|
+
setIdentifier,
|
|
1060
|
+
setOtp,
|
|
1061
|
+
setAcceptedTerms,
|
|
1062
|
+
setError,
|
|
1063
|
+
clearError,
|
|
1064
|
+
setStep,
|
|
1065
|
+
setIsLoading,
|
|
1066
|
+
setTwoFactorSessionId,
|
|
1067
|
+
setShouldPrompt2FA,
|
|
1068
|
+
setTwoFactorCode,
|
|
1069
|
+
setUseBackupCode,
|
|
1070
|
+
startRateLimitCountdown,
|
|
1071
|
+
setWebmail
|
|
1072
|
+
};
|
|
1073
|
+
}, "useAuthFormState");
|
|
1074
|
+
|
|
1075
|
+
// src/auth/hooks/useAuthValidation.ts
|
|
1076
|
+
var import_react5 = require("react");
|
|
1077
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1078
|
+
var useAuthValidation = /* @__PURE__ */ __name(() => {
|
|
1079
|
+
const validateIdentifier2 = (0, import_react5.useCallback)((id) => {
|
|
1080
|
+
return EMAIL_REGEX.test(id);
|
|
1081
|
+
}, []);
|
|
1082
|
+
return { validateIdentifier: validateIdentifier2 };
|
|
1083
|
+
}, "useAuthValidation");
|
|
1084
|
+
var validateIdentifier = /* @__PURE__ */ __name((id) => EMAIL_REGEX.test(id), "validateIdentifier");
|
|
1085
|
+
|
|
1086
|
+
// src/auth/hooks/useAuthForm.ts
|
|
1087
|
+
var import_react9 = require("react");
|
|
1088
|
+
|
|
1089
|
+
// src/auth/utils/logger.ts
|
|
1090
|
+
var import_consola2 = require("consola");
|
|
1091
|
+
var logger = (0, import_consola2.createConsola)({
|
|
1092
|
+
level: getLogLevel()
|
|
1093
|
+
}).withTag("api");
|
|
1094
|
+
onLogLevelChange((level) => {
|
|
1095
|
+
logger.level = level;
|
|
1096
|
+
});
|
|
1097
|
+
var authLogger = logger.withTag("auth");
|
|
1098
|
+
|
|
1099
|
+
// src/auth/hooks/useAutoAuth.ts
|
|
1100
|
+
var import_navigation3 = require("next/navigation");
|
|
1101
|
+
var import_react6 = require("react");
|
|
1102
|
+
|
|
1103
|
+
// src/auth/utils/path.ts
|
|
1104
|
+
var normalizePath = /* @__PURE__ */ __name((path) => {
|
|
1105
|
+
if (!path) return "";
|
|
1106
|
+
const withoutLocale = path.replace(/^\/[a-z]{2}(?=\/|$)/, "");
|
|
1107
|
+
const trimmed = withoutLocale.replace(/\/+$/, "");
|
|
1108
|
+
return trimmed || "/";
|
|
1109
|
+
}, "normalizePath");
|
|
1110
|
+
var isAllowedAuthPath = /* @__PURE__ */ __name((pathname, allowedPaths) => {
|
|
1111
|
+
const normalized = normalizePath(pathname);
|
|
1112
|
+
return allowedPaths.some(
|
|
1113
|
+
(p) => normalized === p || normalized.startsWith(p + "/")
|
|
1114
|
+
);
|
|
1115
|
+
}, "isAllowedAuthPath");
|
|
1116
|
+
|
|
1117
|
+
// src/auth/hooks/useAutoAuth.ts
|
|
1118
|
+
var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
1119
|
+
const { onOTPDetected, cleanupUrl = true, allowedPaths = ["/auth"] } = options;
|
|
1120
|
+
const queryParams = useQueryParams();
|
|
1121
|
+
const pathname = (0, import_navigation3.usePathname)();
|
|
1122
|
+
const router = useCfgRouter();
|
|
1123
|
+
const isAllowedPath = isAllowedAuthPath(pathname, allowedPaths);
|
|
1124
|
+
const queryOtp = queryParams.get("otp") || "";
|
|
1125
|
+
const queryEmail = queryParams.get("email") || void 0;
|
|
1126
|
+
const hasOTP = !!queryOtp;
|
|
1127
|
+
const isReady = !!pathname && hasOTP && isAllowedPath;
|
|
1128
|
+
const otpValid = queryOtp.length === AUTH_CONSTANTS.EMAIL_OTP_LENGTH;
|
|
1129
|
+
const onOTPDetectedRef = (0, import_react6.useRef)(onOTPDetected);
|
|
1130
|
+
onOTPDetectedRef.current = onOTPDetected;
|
|
1131
|
+
(0, import_react6.useEffect)(() => {
|
|
1132
|
+
if (!isReady || !otpValid) return;
|
|
1133
|
+
authLogger.info("OTP detected in URL on auth page:", queryOtp);
|
|
1134
|
+
onOTPDetectedRef.current?.(queryOtp, queryEmail);
|
|
1135
|
+
}, [isReady, otpValid, queryOtp, queryEmail]);
|
|
1136
|
+
(0, import_react6.useEffect)(() => {
|
|
1137
|
+
if (!isReady || !cleanupUrl || !queryOtp) return;
|
|
1138
|
+
const cleanQuery = Object.fromEntries(queryParams.entries());
|
|
1139
|
+
delete cleanQuery.otp;
|
|
1140
|
+
delete cleanQuery.email;
|
|
1141
|
+
const queryString = new URLSearchParams(cleanQuery).toString();
|
|
1142
|
+
const realPathname = typeof window !== "undefined" ? window.location.pathname : pathname;
|
|
1143
|
+
router.replace(queryString ? `${realPathname}?${queryString}` : realPathname);
|
|
1144
|
+
}, [isReady, cleanupUrl, queryOtp]);
|
|
1145
|
+
return {
|
|
1146
|
+
isReady,
|
|
1147
|
+
hasOTP,
|
|
1148
|
+
isAllowedPath
|
|
1149
|
+
};
|
|
1150
|
+
}, "useAutoAuth");
|
|
1151
|
+
|
|
1152
|
+
// src/auth/hooks/useTwoFactor.ts
|
|
1153
|
+
var import_react8 = require("react");
|
|
985
1154
|
|
|
986
1155
|
// src/_api/generated/core/bodySerializer.gen.ts
|
|
987
1156
|
var serializeFormDataPair = /* @__PURE__ */ __name((data, key, value) => {
|
|
@@ -1804,103 +1973,6 @@ var client = createClient(createConfig({ baseUrl: "http://localhost:8000" }));
|
|
|
1804
1973
|
installAuthOnClient(client);
|
|
1805
1974
|
|
|
1806
1975
|
// src/_api/generated/sdk.gen.ts
|
|
1807
|
-
var CfgAccountsApiKey = class {
|
|
1808
|
-
static {
|
|
1809
|
-
__name(this, "CfgAccountsApiKey");
|
|
1810
|
-
}
|
|
1811
|
-
/**
|
|
1812
|
-
* Get API key details
|
|
1813
|
-
*
|
|
1814
|
-
* Retrieve the current user's API key (masked) and metadata.
|
|
1815
|
-
*/
|
|
1816
|
-
static cfgAccountsApiKeyRetrieve(options) {
|
|
1817
|
-
return (options?.client ?? client).get({
|
|
1818
|
-
security: [
|
|
1819
|
-
{ scheme: "bearer", type: "http" },
|
|
1820
|
-
{
|
|
1821
|
-
in: "cookie",
|
|
1822
|
-
name: "sessionid",
|
|
1823
|
-
type: "apiKey"
|
|
1824
|
-
},
|
|
1825
|
-
{ name: "X-API-Key", type: "apiKey" }
|
|
1826
|
-
],
|
|
1827
|
-
url: "/cfg/accounts/api-key/",
|
|
1828
|
-
...options
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
/**
|
|
1832
|
-
* Regenerate API key
|
|
1833
|
-
*
|
|
1834
|
-
* Generate a new API key. The full key is returned only once.
|
|
1835
|
-
*/
|
|
1836
|
-
static cfgAccountsApiKeyRegenerateCreate(options) {
|
|
1837
|
-
return (options.client ?? client).post({
|
|
1838
|
-
security: [
|
|
1839
|
-
{ scheme: "bearer", type: "http" },
|
|
1840
|
-
{
|
|
1841
|
-
in: "cookie",
|
|
1842
|
-
name: "sessionid",
|
|
1843
|
-
type: "apiKey"
|
|
1844
|
-
},
|
|
1845
|
-
{ name: "X-API-Key", type: "apiKey" }
|
|
1846
|
-
],
|
|
1847
|
-
url: "/cfg/accounts/api-key/regenerate/",
|
|
1848
|
-
...options,
|
|
1849
|
-
headers: {
|
|
1850
|
-
"Content-Type": "application/json",
|
|
1851
|
-
...options.headers
|
|
1852
|
-
}
|
|
1853
|
-
});
|
|
1854
|
-
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Reveal API key
|
|
1857
|
-
*
|
|
1858
|
-
* Return the current full API key WITHOUT rotating it. The same durable value every one of the user's agents uses — for the signed-in user to copy and paste into agent onboarding. Default GET stays masked; this is the explicit reveal action.
|
|
1859
|
-
*/
|
|
1860
|
-
static cfgAccountsApiKeyRevealCreate(options) {
|
|
1861
|
-
return (options.client ?? client).post({
|
|
1862
|
-
security: [
|
|
1863
|
-
{ scheme: "bearer", type: "http" },
|
|
1864
|
-
{
|
|
1865
|
-
in: "cookie",
|
|
1866
|
-
name: "sessionid",
|
|
1867
|
-
type: "apiKey"
|
|
1868
|
-
},
|
|
1869
|
-
{ name: "X-API-Key", type: "apiKey" }
|
|
1870
|
-
],
|
|
1871
|
-
url: "/cfg/accounts/api-key/reveal/",
|
|
1872
|
-
...options,
|
|
1873
|
-
headers: {
|
|
1874
|
-
"Content-Type": "application/json",
|
|
1875
|
-
...options.headers
|
|
1876
|
-
}
|
|
1877
|
-
});
|
|
1878
|
-
}
|
|
1879
|
-
/**
|
|
1880
|
-
* Test API key
|
|
1881
|
-
*
|
|
1882
|
-
* Test whether an API key is valid without consuming it.
|
|
1883
|
-
*/
|
|
1884
|
-
static cfgAccountsApiKeyTestCreate(options) {
|
|
1885
|
-
return (options.client ?? client).post({
|
|
1886
|
-
security: [
|
|
1887
|
-
{ scheme: "bearer", type: "http" },
|
|
1888
|
-
{
|
|
1889
|
-
in: "cookie",
|
|
1890
|
-
name: "sessionid",
|
|
1891
|
-
type: "apiKey"
|
|
1892
|
-
},
|
|
1893
|
-
{ name: "X-API-Key", type: "apiKey" }
|
|
1894
|
-
],
|
|
1895
|
-
url: "/cfg/accounts/api-key/test/",
|
|
1896
|
-
...options,
|
|
1897
|
-
headers: {
|
|
1898
|
-
"Content-Type": "application/json",
|
|
1899
|
-
...options.headers
|
|
1900
|
-
}
|
|
1901
|
-
});
|
|
1902
|
-
}
|
|
1903
|
-
};
|
|
1904
1976
|
var CfgAccountsOauth = class {
|
|
1905
1977
|
static {
|
|
1906
1978
|
__name(this, "CfgAccountsOauth");
|
|
@@ -1912,7 +1984,11 @@ var CfgAccountsOauth = class {
|
|
|
1912
1984
|
*/
|
|
1913
1985
|
static cfgAccountsOauthConnectionsList(options) {
|
|
1914
1986
|
return (options?.client ?? client).get({
|
|
1915
|
-
security: [
|
|
1987
|
+
security: [
|
|
1988
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
1989
|
+
{ scheme: "bearer", type: "http" },
|
|
1990
|
+
{ name: "Authorization", type: "apiKey" }
|
|
1991
|
+
],
|
|
1916
1992
|
url: "/cfg/accounts/oauth/connections/",
|
|
1917
1993
|
...options
|
|
1918
1994
|
});
|
|
@@ -1924,7 +2000,11 @@ var CfgAccountsOauth = class {
|
|
|
1924
2000
|
*/
|
|
1925
2001
|
static cfgAccountsOauthDisconnectCreate(options) {
|
|
1926
2002
|
return (options.client ?? client).post({
|
|
1927
|
-
security: [
|
|
2003
|
+
security: [
|
|
2004
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2005
|
+
{ scheme: "bearer", type: "http" },
|
|
2006
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2007
|
+
],
|
|
1928
2008
|
url: "/cfg/accounts/oauth/disconnect/",
|
|
1929
2009
|
...options,
|
|
1930
2010
|
headers: {
|
|
@@ -1981,7 +2061,11 @@ var CfgAccounts = class {
|
|
|
1981
2061
|
*/
|
|
1982
2062
|
static cfgAccountsOtpRequestCreate(options) {
|
|
1983
2063
|
return (options.client ?? client).post({
|
|
1984
|
-
security: [
|
|
2064
|
+
security: [
|
|
2065
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2066
|
+
{ scheme: "bearer", type: "http" },
|
|
2067
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2068
|
+
],
|
|
1985
2069
|
url: "/cfg/accounts/otp/request/",
|
|
1986
2070
|
...options,
|
|
1987
2071
|
headers: {
|
|
@@ -2002,7 +2086,11 @@ var CfgAccounts = class {
|
|
|
2002
2086
|
*/
|
|
2003
2087
|
static cfgAccountsOtpVerifyCreate(options) {
|
|
2004
2088
|
return (options.client ?? client).post({
|
|
2005
|
-
security: [
|
|
2089
|
+
security: [
|
|
2090
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2091
|
+
{ scheme: "bearer", type: "http" },
|
|
2092
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2093
|
+
],
|
|
2006
2094
|
url: "/cfg/accounts/otp/verify/",
|
|
2007
2095
|
...options,
|
|
2008
2096
|
headers: {
|
|
@@ -2040,7 +2128,11 @@ var CfgAccountsProfile = class {
|
|
|
2040
2128
|
static cfgAccountsProfileAvatarCreate(options) {
|
|
2041
2129
|
return (options?.client ?? client).post({
|
|
2042
2130
|
...formDataBodySerializer,
|
|
2043
|
-
security: [
|
|
2131
|
+
security: [
|
|
2132
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2133
|
+
{ scheme: "bearer", type: "http" },
|
|
2134
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2135
|
+
],
|
|
2044
2136
|
url: "/cfg/accounts/profile/avatar/",
|
|
2045
2137
|
...options,
|
|
2046
2138
|
headers: {
|
|
@@ -2161,17 +2253,15 @@ var CfgAccountsAuth = class {
|
|
|
2161
2253
|
__name(this, "CfgAccountsAuth");
|
|
2162
2254
|
}
|
|
2163
2255
|
/**
|
|
2164
|
-
*
|
|
2256
|
+
* Revoke a refresh token (logout).
|
|
2165
2257
|
*
|
|
2166
|
-
*
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
* proof is required on the refresh request — so a stolen refresh token can't
|
|
2170
|
-
* be used to mint fresh tokens.
|
|
2258
|
+
* Blacklists the posted refresh token so it can never mint another access
|
|
2259
|
+
* token. Called best-effort by the client's logout — without it, "logout"
|
|
2260
|
+
* is purely client-side and a stolen refresh token survives until expiry.
|
|
2171
2261
|
*/
|
|
2172
|
-
static
|
|
2262
|
+
static cfgAccountsTokenBlacklistCreate(options) {
|
|
2173
2263
|
return (options.client ?? client).post({
|
|
2174
|
-
url: "/cfg/accounts/token/
|
|
2264
|
+
url: "/cfg/accounts/token/blacklist/",
|
|
2175
2265
|
...options,
|
|
2176
2266
|
headers: {
|
|
2177
2267
|
"Content-Type": "application/json",
|
|
@@ -2179,48 +2269,18 @@ var CfgAccountsAuth = class {
|
|
|
2179
2269
|
}
|
|
2180
2270
|
});
|
|
2181
2271
|
}
|
|
2182
|
-
};
|
|
2183
|
-
var CfgCentrifugo = class {
|
|
2184
|
-
static {
|
|
2185
|
-
__name(this, "CfgCentrifugo");
|
|
2186
|
-
}
|
|
2187
|
-
/**
|
|
2188
|
-
* Get Centrifugo connection token
|
|
2189
|
-
*
|
|
2190
|
-
* Generate JWT token for WebSocket connection to Centrifugo. Token includes user's allowed channels based on their permissions. Requires authentication.
|
|
2191
|
-
*/
|
|
2192
|
-
static cfgCentrifugoAuthTokenRetrieve(options) {
|
|
2193
|
-
return (options?.client ?? client).get({
|
|
2194
|
-
security: [{ name: "X-API-Key", type: "apiKey" }, { scheme: "bearer", type: "http" }],
|
|
2195
|
-
url: "/cfg/centrifugo/auth/token/",
|
|
2196
|
-
...options
|
|
2197
|
-
});
|
|
2198
|
-
}
|
|
2199
|
-
};
|
|
2200
|
-
var CfgTotpBackupCodes = class {
|
|
2201
|
-
static {
|
|
2202
|
-
__name(this, "CfgTotpBackupCodes");
|
|
2203
|
-
}
|
|
2204
|
-
/**
|
|
2205
|
-
* Get backup codes status for user.
|
|
2206
|
-
*/
|
|
2207
|
-
static cfgTotpBackupCodesRetrieve(options) {
|
|
2208
|
-
return (options?.client ?? client).get({
|
|
2209
|
-
security: [{ name: "X-API-Key", type: "apiKey" }, { scheme: "bearer", type: "http" }],
|
|
2210
|
-
url: "/cfg/totp/backup-codes/",
|
|
2211
|
-
...options
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
2272
|
/**
|
|
2215
|
-
*
|
|
2273
|
+
* Refresh JWT token.
|
|
2216
2274
|
*
|
|
2217
|
-
*
|
|
2218
|
-
*
|
|
2275
|
+
* DPoP-aware: when the incoming refresh token is key-bound (`cnf.jkt`), the
|
|
2276
|
+
* rotated access/refresh in the response are re-stamped with the same `cnf`
|
|
2277
|
+
* (stock SimpleJWT drops it from the derived access), and a matching DPoP
|
|
2278
|
+
* proof is required on the refresh request — so a stolen refresh token can't
|
|
2279
|
+
* be used to mint fresh tokens.
|
|
2219
2280
|
*/
|
|
2220
|
-
static
|
|
2281
|
+
static cfgAccountsTokenRefreshCreate(options) {
|
|
2221
2282
|
return (options.client ?? client).post({
|
|
2222
|
-
|
|
2223
|
-
url: "/cfg/totp/backup-codes/regenerate/",
|
|
2283
|
+
url: "/cfg/accounts/token/refresh/",
|
|
2224
2284
|
...options,
|
|
2225
2285
|
headers: {
|
|
2226
2286
|
"Content-Type": "application/json",
|
|
@@ -2238,7 +2298,11 @@ var CfgTotp = class {
|
|
|
2238
2298
|
*/
|
|
2239
2299
|
static cfgTotpDevicesRetrieve(options) {
|
|
2240
2300
|
return (options?.client ?? client).get({
|
|
2241
|
-
security: [
|
|
2301
|
+
security: [
|
|
2302
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2303
|
+
{ scheme: "bearer", type: "http" },
|
|
2304
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2305
|
+
],
|
|
2242
2306
|
url: "/cfg/totp/devices/",
|
|
2243
2307
|
...options
|
|
2244
2308
|
});
|
|
@@ -2250,7 +2314,11 @@ var CfgTotp = class {
|
|
|
2250
2314
|
*/
|
|
2251
2315
|
static cfgTotpDevicesDestroy(options) {
|
|
2252
2316
|
return (options.client ?? client).delete({
|
|
2253
|
-
security: [
|
|
2317
|
+
security: [
|
|
2318
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2319
|
+
{ scheme: "bearer", type: "http" },
|
|
2320
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2321
|
+
],
|
|
2254
2322
|
url: "/cfg/totp/devices/{id}/",
|
|
2255
2323
|
...options
|
|
2256
2324
|
});
|
|
@@ -2262,7 +2330,11 @@ var CfgTotp = class {
|
|
|
2262
2330
|
*/
|
|
2263
2331
|
static cfgTotpDisableCreate(options) {
|
|
2264
2332
|
return (options.client ?? client).post({
|
|
2265
|
-
security: [
|
|
2333
|
+
security: [
|
|
2334
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2335
|
+
{ scheme: "bearer", type: "http" },
|
|
2336
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2337
|
+
],
|
|
2266
2338
|
url: "/cfg/totp/disable/",
|
|
2267
2339
|
...options,
|
|
2268
2340
|
headers: {
|
|
@@ -2283,298 +2355,86 @@ var CfgTotpSetup = class {
|
|
|
2283
2355
|
*/
|
|
2284
2356
|
static cfgTotpSetupCreate(options) {
|
|
2285
2357
|
return (options?.client ?? client).post({
|
|
2286
|
-
security: [
|
|
2358
|
+
security: [
|
|
2359
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2360
|
+
{ scheme: "bearer", type: "http" },
|
|
2361
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2362
|
+
],
|
|
2287
2363
|
url: "/cfg/totp/setup/",
|
|
2288
2364
|
...options,
|
|
2289
2365
|
headers: {
|
|
2290
|
-
"Content-Type": "application/json",
|
|
2291
|
-
...options?.headers
|
|
2292
|
-
}
|
|
2293
|
-
});
|
|
2294
|
-
}
|
|
2295
|
-
/**
|
|
2296
|
-
* Confirm 2FA setup with first valid code.
|
|
2297
|
-
*
|
|
2298
|
-
* Activates the device and generates backup codes.
|
|
2299
|
-
*/
|
|
2300
|
-
static cfgTotpSetupConfirmCreate(options) {
|
|
2301
|
-
return (options.client ?? client).post({
|
|
2302
|
-
security: [{ name: "X-API-Key", type: "apiKey" }, { scheme: "bearer", type: "http" }],
|
|
2303
|
-
url: "/cfg/totp/setup/confirm/",
|
|
2304
|
-
...options,
|
|
2305
|
-
headers: {
|
|
2306
|
-
"Content-Type": "application/json",
|
|
2307
|
-
...options.headers
|
|
2308
|
-
}
|
|
2309
|
-
});
|
|
2310
|
-
}
|
|
2311
|
-
};
|
|
2312
|
-
var CfgTotpVerify = class {
|
|
2313
|
-
static {
|
|
2314
|
-
__name(this, "CfgTotpVerify");
|
|
2315
|
-
}
|
|
2316
|
-
/**
|
|
2317
|
-
* Verify TOTP code for 2FA session.
|
|
2318
|
-
*
|
|
2319
|
-
* Completes authentication and returns JWT tokens on success.
|
|
2320
|
-
*/
|
|
2321
|
-
static cfgTotpVerifyCreate(options) {
|
|
2322
|
-
return (options.client ?? client).post({
|
|
2323
|
-
security: [{ name: "X-API-Key", type: "apiKey" }, { scheme: "bearer", type: "http" }],
|
|
2324
|
-
url: "/cfg/totp/verify/",
|
|
2325
|
-
...options,
|
|
2326
|
-
headers: {
|
|
2327
|
-
"Content-Type": "application/json",
|
|
2328
|
-
...options.headers
|
|
2329
|
-
}
|
|
2330
|
-
});
|
|
2331
|
-
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Verify backup recovery code for 2FA session.
|
|
2334
|
-
*
|
|
2335
|
-
* Alternative verification method when TOTP device unavailable.
|
|
2336
|
-
*/
|
|
2337
|
-
static cfgTotpVerifyBackupCreate(options) {
|
|
2338
|
-
return (options.client ?? client).post({
|
|
2339
|
-
security: [{ name: "X-API-Key", type: "apiKey" }, { scheme: "bearer", type: "http" }],
|
|
2340
|
-
url: "/cfg/totp/verify/backup/",
|
|
2341
|
-
...options,
|
|
2342
|
-
headers: {
|
|
2343
|
-
"Content-Type": "application/json",
|
|
2344
|
-
...options.headers
|
|
2345
|
-
}
|
|
2346
|
-
});
|
|
2347
|
-
}
|
|
2348
|
-
};
|
|
2349
|
-
|
|
2350
|
-
// src/_api/generated/_cfg_accounts/api.ts
|
|
2351
|
-
var API = class {
|
|
2352
|
-
static {
|
|
2353
|
-
__name(this, "API");
|
|
2354
|
-
}
|
|
2355
|
-
logger;
|
|
2356
|
-
cfgAccountsApiKey = CfgAccountsApiKey;
|
|
2357
|
-
cfgAccountsOauth = CfgAccountsOauth;
|
|
2358
|
-
cfgAccounts = CfgAccounts;
|
|
2359
|
-
cfgAccountsProfile = CfgAccountsProfile;
|
|
2360
|
-
cfgAccountsAuth = CfgAccountsAuth;
|
|
2361
|
-
constructor(_baseUrl, opts = {}) {
|
|
2362
|
-
this.logger = new APILogger(opts.logger);
|
|
2363
|
-
if (_baseUrl) auth.setBaseUrl(_baseUrl);
|
|
2364
|
-
if (opts.locale !== void 0) auth.setLocale(opts.locale);
|
|
2365
|
-
if (opts.apiKey !== void 0) auth.setApiKey(opts.apiKey);
|
|
2366
|
-
if (opts.withCredentials !== void 0) auth.setWithCredentials(opts.withCredentials);
|
|
2367
|
-
}
|
|
2368
|
-
// ── Base URL ────────────────────────────────────────────────────────────
|
|
2369
|
-
getBaseUrl() {
|
|
2370
|
-
return auth.getBaseUrl();
|
|
2371
|
-
}
|
|
2372
|
-
setBaseUrl(url) {
|
|
2373
|
-
auth.setBaseUrl(url);
|
|
2374
|
-
}
|
|
2375
|
-
// ── Tokens ──────────────────────────────────────────────────────────────
|
|
2376
|
-
getToken() {
|
|
2377
|
-
return auth.getToken();
|
|
2378
|
-
}
|
|
2379
|
-
setToken(token) {
|
|
2380
|
-
auth.setToken(token);
|
|
2381
|
-
}
|
|
2382
|
-
getRefreshToken() {
|
|
2383
|
-
return auth.getRefreshToken();
|
|
2384
|
-
}
|
|
2385
|
-
setRefreshToken(token) {
|
|
2386
|
-
auth.setRefreshToken(token);
|
|
2387
|
-
}
|
|
2388
|
-
clearToken() {
|
|
2389
|
-
auth.clearTokens();
|
|
2390
|
-
}
|
|
2391
|
-
isAuthenticated() {
|
|
2392
|
-
return auth.isAuthenticated();
|
|
2393
|
-
}
|
|
2394
|
-
// ── Locale / API key ────────────────────────────────────────────────────
|
|
2395
|
-
getLocale() {
|
|
2396
|
-
return auth.getLocale();
|
|
2397
|
-
}
|
|
2398
|
-
setLocale(locale) {
|
|
2399
|
-
auth.setLocale(locale);
|
|
2400
|
-
}
|
|
2401
|
-
getApiKey() {
|
|
2402
|
-
return auth.getApiKey();
|
|
2403
|
-
}
|
|
2404
|
-
setApiKey(key) {
|
|
2405
|
-
auth.setApiKey(key);
|
|
2406
|
-
}
|
|
2407
|
-
// ── 401 handling ────────────────────────────────────────────────────────
|
|
2408
|
-
/** Fired only on terminal 401 (after refresh+retry path is exhausted). */
|
|
2409
|
-
onUnauthorized(cb) {
|
|
2410
|
-
auth.onUnauthorized(cb);
|
|
2411
|
-
}
|
|
2412
|
-
/** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
|
|
2413
|
-
setRefreshHandler(fn) {
|
|
2414
|
-
auth.setRefreshHandler(fn);
|
|
2415
|
-
}
|
|
2416
|
-
/**
|
|
2417
|
-
* Proactively refresh now via the registered handler, sharing the single
|
|
2418
|
-
* 401-recovery flight (rotation + dedup + rotated-token persistence).
|
|
2419
|
-
* See `auth.refreshNow`.
|
|
2420
|
-
*/
|
|
2421
|
-
refreshNow() {
|
|
2422
|
-
return auth.refreshNow();
|
|
2423
|
-
}
|
|
2424
|
-
};
|
|
2425
|
-
|
|
2426
|
-
// src/_api/generated/_cfg_centrifugo/api.ts
|
|
2427
|
-
var API2 = class {
|
|
2428
|
-
static {
|
|
2429
|
-
__name(this, "API");
|
|
2430
|
-
}
|
|
2431
|
-
logger;
|
|
2432
|
-
cfgCentrifugo = CfgCentrifugo;
|
|
2433
|
-
constructor(_baseUrl, opts = {}) {
|
|
2434
|
-
this.logger = new APILogger(opts.logger);
|
|
2435
|
-
if (_baseUrl) auth.setBaseUrl(_baseUrl);
|
|
2436
|
-
if (opts.locale !== void 0) auth.setLocale(opts.locale);
|
|
2437
|
-
if (opts.apiKey !== void 0) auth.setApiKey(opts.apiKey);
|
|
2438
|
-
if (opts.withCredentials !== void 0) auth.setWithCredentials(opts.withCredentials);
|
|
2439
|
-
}
|
|
2440
|
-
// ── Base URL ────────────────────────────────────────────────────────────
|
|
2441
|
-
getBaseUrl() {
|
|
2442
|
-
return auth.getBaseUrl();
|
|
2443
|
-
}
|
|
2444
|
-
setBaseUrl(url) {
|
|
2445
|
-
auth.setBaseUrl(url);
|
|
2446
|
-
}
|
|
2447
|
-
// ── Tokens ──────────────────────────────────────────────────────────────
|
|
2448
|
-
getToken() {
|
|
2449
|
-
return auth.getToken();
|
|
2450
|
-
}
|
|
2451
|
-
setToken(token) {
|
|
2452
|
-
auth.setToken(token);
|
|
2453
|
-
}
|
|
2454
|
-
getRefreshToken() {
|
|
2455
|
-
return auth.getRefreshToken();
|
|
2456
|
-
}
|
|
2457
|
-
setRefreshToken(token) {
|
|
2458
|
-
auth.setRefreshToken(token);
|
|
2459
|
-
}
|
|
2460
|
-
clearToken() {
|
|
2461
|
-
auth.clearTokens();
|
|
2462
|
-
}
|
|
2463
|
-
isAuthenticated() {
|
|
2464
|
-
return auth.isAuthenticated();
|
|
2465
|
-
}
|
|
2466
|
-
// ── Locale / API key ────────────────────────────────────────────────────
|
|
2467
|
-
getLocale() {
|
|
2468
|
-
return auth.getLocale();
|
|
2469
|
-
}
|
|
2470
|
-
setLocale(locale) {
|
|
2471
|
-
auth.setLocale(locale);
|
|
2472
|
-
}
|
|
2473
|
-
getApiKey() {
|
|
2474
|
-
return auth.getApiKey();
|
|
2475
|
-
}
|
|
2476
|
-
setApiKey(key) {
|
|
2477
|
-
auth.setApiKey(key);
|
|
2478
|
-
}
|
|
2479
|
-
// ── 401 handling ────────────────────────────────────────────────────────
|
|
2480
|
-
/** Fired only on terminal 401 (after refresh+retry path is exhausted). */
|
|
2481
|
-
onUnauthorized(cb) {
|
|
2482
|
-
auth.onUnauthorized(cb);
|
|
2483
|
-
}
|
|
2484
|
-
/** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
|
|
2485
|
-
setRefreshHandler(fn) {
|
|
2486
|
-
auth.setRefreshHandler(fn);
|
|
2366
|
+
"Content-Type": "application/json",
|
|
2367
|
+
...options?.headers
|
|
2368
|
+
}
|
|
2369
|
+
});
|
|
2487
2370
|
}
|
|
2488
2371
|
/**
|
|
2489
|
-
*
|
|
2490
|
-
*
|
|
2491
|
-
*
|
|
2372
|
+
* Confirm 2FA setup with first valid code.
|
|
2373
|
+
*
|
|
2374
|
+
* Activates the device and generates backup codes.
|
|
2492
2375
|
*/
|
|
2493
|
-
|
|
2494
|
-
return
|
|
2376
|
+
static cfgTotpSetupConfirmCreate(options) {
|
|
2377
|
+
return (options.client ?? client).post({
|
|
2378
|
+
security: [
|
|
2379
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2380
|
+
{ scheme: "bearer", type: "http" },
|
|
2381
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2382
|
+
],
|
|
2383
|
+
url: "/cfg/totp/setup/confirm/",
|
|
2384
|
+
...options,
|
|
2385
|
+
headers: {
|
|
2386
|
+
"Content-Type": "application/json",
|
|
2387
|
+
...options.headers
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2495
2390
|
}
|
|
2496
2391
|
};
|
|
2497
|
-
|
|
2498
|
-
// src/_api/generated/_cfg_totp/api.ts
|
|
2499
|
-
var API3 = class {
|
|
2392
|
+
var CfgTotpVerify = class {
|
|
2500
2393
|
static {
|
|
2501
|
-
__name(this, "
|
|
2502
|
-
}
|
|
2503
|
-
logger;
|
|
2504
|
-
cfgTotpBackupCodes = CfgTotpBackupCodes;
|
|
2505
|
-
cfgTotp = CfgTotp;
|
|
2506
|
-
cfgTotpSetup = CfgTotpSetup;
|
|
2507
|
-
cfgTotpVerify = CfgTotpVerify;
|
|
2508
|
-
constructor(_baseUrl, opts = {}) {
|
|
2509
|
-
this.logger = new APILogger(opts.logger);
|
|
2510
|
-
if (_baseUrl) auth.setBaseUrl(_baseUrl);
|
|
2511
|
-
if (opts.locale !== void 0) auth.setLocale(opts.locale);
|
|
2512
|
-
if (opts.apiKey !== void 0) auth.setApiKey(opts.apiKey);
|
|
2513
|
-
if (opts.withCredentials !== void 0) auth.setWithCredentials(opts.withCredentials);
|
|
2514
|
-
}
|
|
2515
|
-
// ── Base URL ────────────────────────────────────────────────────────────
|
|
2516
|
-
getBaseUrl() {
|
|
2517
|
-
return auth.getBaseUrl();
|
|
2518
|
-
}
|
|
2519
|
-
setBaseUrl(url) {
|
|
2520
|
-
auth.setBaseUrl(url);
|
|
2521
|
-
}
|
|
2522
|
-
// ── Tokens ──────────────────────────────────────────────────────────────
|
|
2523
|
-
getToken() {
|
|
2524
|
-
return auth.getToken();
|
|
2525
|
-
}
|
|
2526
|
-
setToken(token) {
|
|
2527
|
-
auth.setToken(token);
|
|
2528
|
-
}
|
|
2529
|
-
getRefreshToken() {
|
|
2530
|
-
return auth.getRefreshToken();
|
|
2531
|
-
}
|
|
2532
|
-
setRefreshToken(token) {
|
|
2533
|
-
auth.setRefreshToken(token);
|
|
2534
|
-
}
|
|
2535
|
-
clearToken() {
|
|
2536
|
-
auth.clearTokens();
|
|
2537
|
-
}
|
|
2538
|
-
isAuthenticated() {
|
|
2539
|
-
return auth.isAuthenticated();
|
|
2540
|
-
}
|
|
2541
|
-
// ── Locale / API key ────────────────────────────────────────────────────
|
|
2542
|
-
getLocale() {
|
|
2543
|
-
return auth.getLocale();
|
|
2544
|
-
}
|
|
2545
|
-
setLocale(locale) {
|
|
2546
|
-
auth.setLocale(locale);
|
|
2547
|
-
}
|
|
2548
|
-
getApiKey() {
|
|
2549
|
-
return auth.getApiKey();
|
|
2550
|
-
}
|
|
2551
|
-
setApiKey(key) {
|
|
2552
|
-
auth.setApiKey(key);
|
|
2553
|
-
}
|
|
2554
|
-
// ── 401 handling ────────────────────────────────────────────────────────
|
|
2555
|
-
/** Fired only on terminal 401 (after refresh+retry path is exhausted). */
|
|
2556
|
-
onUnauthorized(cb) {
|
|
2557
|
-
auth.onUnauthorized(cb);
|
|
2394
|
+
__name(this, "CfgTotpVerify");
|
|
2558
2395
|
}
|
|
2559
|
-
/**
|
|
2560
|
-
|
|
2561
|
-
|
|
2396
|
+
/**
|
|
2397
|
+
* Verify TOTP code for 2FA session.
|
|
2398
|
+
*
|
|
2399
|
+
* Completes authentication and returns JWT tokens on success.
|
|
2400
|
+
*/
|
|
2401
|
+
static cfgTotpVerifyCreate(options) {
|
|
2402
|
+
return (options.client ?? client).post({
|
|
2403
|
+
security: [
|
|
2404
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2405
|
+
{ scheme: "bearer", type: "http" },
|
|
2406
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2407
|
+
],
|
|
2408
|
+
url: "/cfg/totp/verify/",
|
|
2409
|
+
...options,
|
|
2410
|
+
headers: {
|
|
2411
|
+
"Content-Type": "application/json",
|
|
2412
|
+
...options.headers
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2562
2415
|
}
|
|
2563
2416
|
/**
|
|
2564
|
-
*
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
2417
|
+
* Verify backup recovery code for 2FA session.
|
|
2418
|
+
*
|
|
2419
|
+
* Alternative verification method when TOTP device unavailable.
|
|
2567
2420
|
*/
|
|
2568
|
-
|
|
2569
|
-
return
|
|
2421
|
+
static cfgTotpVerifyBackupCreate(options) {
|
|
2422
|
+
return (options.client ?? client).post({
|
|
2423
|
+
security: [
|
|
2424
|
+
{ name: "X-API-Key", type: "apiKey" },
|
|
2425
|
+
{ scheme: "bearer", type: "http" },
|
|
2426
|
+
{ name: "Authorization", type: "apiKey" }
|
|
2427
|
+
],
|
|
2428
|
+
url: "/cfg/totp/verify/backup/",
|
|
2429
|
+
...options,
|
|
2430
|
+
headers: {
|
|
2431
|
+
"Content-Type": "application/json",
|
|
2432
|
+
...options.headers
|
|
2433
|
+
}
|
|
2434
|
+
});
|
|
2570
2435
|
}
|
|
2571
2436
|
};
|
|
2572
2437
|
|
|
2573
|
-
// src/_api/generated/index.ts
|
|
2574
|
-
var CfgAccountsApi = new API();
|
|
2575
|
-
var CfgCentrifugoApi = new API2();
|
|
2576
|
-
var CfgTotpApi = new API3();
|
|
2577
|
-
|
|
2578
2438
|
// src/auth/utils/analytics.ts
|
|
2579
2439
|
var AnalyticsEvent = /* @__PURE__ */ ((AnalyticsEvent2) => {
|
|
2580
2440
|
AnalyticsEvent2["AUTH_OTP_REQUEST"] = "auth_otp_request";
|
|
@@ -2608,24 +2468,229 @@ var Analytics = {
|
|
|
2608
2468
|
if (isDev) {
|
|
2609
2469
|
console.log("[Analytics] Set user:", userId);
|
|
2610
2470
|
}
|
|
2611
|
-
}
|
|
2612
|
-
};
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
|
|
2474
|
+
// src/auth/hooks/useSessionStorage.ts
|
|
2475
|
+
var import_react7 = require("react");
|
|
2476
|
+
function useSessionStorage(key, initialValue) {
|
|
2477
|
+
const [storedValue, setStoredValue] = (0, import_react7.useState)(() => {
|
|
2478
|
+
if (typeof window === "undefined") {
|
|
2479
|
+
return initialValue;
|
|
2480
|
+
}
|
|
2481
|
+
try {
|
|
2482
|
+
const item = window.sessionStorage.getItem(key);
|
|
2483
|
+
return item ? JSON.parse(item) : initialValue;
|
|
2484
|
+
} catch (error) {
|
|
2485
|
+
authLogger.error(`Error reading sessionStorage key "${key}":`, error);
|
|
2486
|
+
return initialValue;
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
const checkDataSize = /* @__PURE__ */ __name((data) => {
|
|
2490
|
+
try {
|
|
2491
|
+
const jsonString = JSON.stringify(data);
|
|
2492
|
+
const sizeInBytes = new Blob([jsonString]).size;
|
|
2493
|
+
const sizeInKB = sizeInBytes / 1024;
|
|
2494
|
+
if (sizeInKB > 1024) {
|
|
2495
|
+
authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
|
|
2496
|
+
return false;
|
|
2497
|
+
}
|
|
2498
|
+
return true;
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
authLogger.error(`Error checking data size for key "${key}":`, error);
|
|
2501
|
+
return false;
|
|
2502
|
+
}
|
|
2503
|
+
}, "checkDataSize");
|
|
2504
|
+
const clearOldData = /* @__PURE__ */ __name(() => {
|
|
2505
|
+
try {
|
|
2506
|
+
const keys = Object.keys(sessionStorage).filter((key2) => key2 && typeof key2 === "string");
|
|
2507
|
+
if (keys.length > 50) {
|
|
2508
|
+
const itemsToRemove = Math.ceil(keys.length * 0.2);
|
|
2509
|
+
for (let i = 0; i < itemsToRemove; i++) {
|
|
2510
|
+
try {
|
|
2511
|
+
const key2 = keys[i];
|
|
2512
|
+
if (key2) {
|
|
2513
|
+
sessionStorage.removeItem(key2);
|
|
2514
|
+
sessionStorage.removeItem(`${key2}_timestamp`);
|
|
2515
|
+
}
|
|
2516
|
+
} catch {
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
} catch (error) {
|
|
2521
|
+
authLogger.error("Error clearing old sessionStorage data:", error);
|
|
2522
|
+
}
|
|
2523
|
+
}, "clearOldData");
|
|
2524
|
+
const forceClearAll = /* @__PURE__ */ __name(() => {
|
|
2525
|
+
try {
|
|
2526
|
+
const keys = Object.keys(sessionStorage);
|
|
2527
|
+
for (const key2 of keys) {
|
|
2528
|
+
try {
|
|
2529
|
+
sessionStorage.removeItem(key2);
|
|
2530
|
+
} catch {
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
} catch (error) {
|
|
2534
|
+
authLogger.error("Error force clearing sessionStorage:", error);
|
|
2535
|
+
}
|
|
2536
|
+
}, "forceClearAll");
|
|
2537
|
+
const setValue = /* @__PURE__ */ __name((value) => {
|
|
2538
|
+
try {
|
|
2539
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
2540
|
+
if (!checkDataSize(valueToStore)) {
|
|
2541
|
+
authLogger.warn(`Data size too large for key "${key}", removing key`);
|
|
2542
|
+
try {
|
|
2543
|
+
window.sessionStorage.removeItem(key);
|
|
2544
|
+
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
2545
|
+
} catch {
|
|
2546
|
+
}
|
|
2547
|
+
setStoredValue(valueToStore);
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
setStoredValue(valueToStore);
|
|
2551
|
+
if (typeof window !== "undefined") {
|
|
2552
|
+
try {
|
|
2553
|
+
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
2554
|
+
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
2555
|
+
} catch (storageError) {
|
|
2556
|
+
if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
|
|
2557
|
+
authLogger.warn("sessionStorage quota exceeded, clearing old data...");
|
|
2558
|
+
clearOldData();
|
|
2559
|
+
try {
|
|
2560
|
+
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
2561
|
+
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
2562
|
+
} catch (retryError) {
|
|
2563
|
+
authLogger.error(`Failed to set sessionStorage key "${key}" after clearing old data:`, retryError);
|
|
2564
|
+
try {
|
|
2565
|
+
forceClearAll();
|
|
2566
|
+
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
2567
|
+
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
2568
|
+
} catch (finalError) {
|
|
2569
|
+
authLogger.error(`Failed to set sessionStorage key "${key}" after force clearing:`, finalError);
|
|
2570
|
+
setStoredValue(valueToStore);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
} else {
|
|
2574
|
+
throw storageError;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
authLogger.error(`Error setting sessionStorage key "${key}":`, error);
|
|
2580
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
2581
|
+
setStoredValue(valueToStore);
|
|
2582
|
+
}
|
|
2583
|
+
}, "setValue");
|
|
2584
|
+
const removeValue = /* @__PURE__ */ __name(() => {
|
|
2585
|
+
try {
|
|
2586
|
+
setStoredValue(initialValue);
|
|
2587
|
+
if (typeof window !== "undefined") {
|
|
2588
|
+
try {
|
|
2589
|
+
window.sessionStorage.removeItem(key);
|
|
2590
|
+
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
2591
|
+
} catch (removeError) {
|
|
2592
|
+
if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
|
|
2593
|
+
authLogger.warn("sessionStorage quota exceeded during removal, clearing old data...");
|
|
2594
|
+
clearOldData();
|
|
2595
|
+
try {
|
|
2596
|
+
window.sessionStorage.removeItem(key);
|
|
2597
|
+
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
2598
|
+
} catch (retryError) {
|
|
2599
|
+
authLogger.error(`Failed to remove sessionStorage key "${key}" after clearing:`, retryError);
|
|
2600
|
+
forceClearAll();
|
|
2601
|
+
}
|
|
2602
|
+
} else {
|
|
2603
|
+
throw removeError;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
} catch (error) {
|
|
2608
|
+
authLogger.error(`Error removing sessionStorage key "${key}":`, error);
|
|
2609
|
+
}
|
|
2610
|
+
}, "removeValue");
|
|
2611
|
+
return [storedValue, setValue, removeValue];
|
|
2612
|
+
}
|
|
2613
|
+
__name(useSessionStorage, "useSessionStorage");
|
|
2614
|
+
|
|
2615
|
+
// src/auth/hooks/useAuthRedirect.ts
|
|
2616
|
+
var AUTH_REDIRECT_KEY = "auth_redirect_url";
|
|
2617
|
+
var getRedirectFromStorage = /* @__PURE__ */ __name(() => {
|
|
2618
|
+
if (typeof window === "undefined") return "";
|
|
2619
|
+
try {
|
|
2620
|
+
const item = window.sessionStorage.getItem(AUTH_REDIRECT_KEY);
|
|
2621
|
+
return item ? JSON.parse(item) : "";
|
|
2622
|
+
} catch {
|
|
2623
|
+
return "";
|
|
2624
|
+
}
|
|
2625
|
+
}, "getRedirectFromStorage");
|
|
2626
|
+
var clearRedirectFromStorage = /* @__PURE__ */ __name(() => {
|
|
2627
|
+
if (typeof window === "undefined") return;
|
|
2628
|
+
try {
|
|
2629
|
+
window.sessionStorage.removeItem(AUTH_REDIRECT_KEY);
|
|
2630
|
+
window.sessionStorage.removeItem(`${AUTH_REDIRECT_KEY}_timestamp`);
|
|
2631
|
+
} catch {
|
|
2632
|
+
}
|
|
2633
|
+
}, "clearRedirectFromStorage");
|
|
2634
|
+
var peekSavedRedirect = /* @__PURE__ */ __name(() => getRedirectFromStorage() || null, "peekSavedRedirect");
|
|
2635
|
+
var consumeSavedRedirect = /* @__PURE__ */ __name(() => {
|
|
2636
|
+
const stored = getRedirectFromStorage();
|
|
2637
|
+
if (stored) clearRedirectFromStorage();
|
|
2638
|
+
return stored || null;
|
|
2639
|
+
}, "consumeSavedRedirect");
|
|
2640
|
+
var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
|
|
2641
|
+
const { fallbackUrl = "/dashboard", clearOnUse = true } = options;
|
|
2642
|
+
const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage(AUTH_REDIRECT_KEY, "");
|
|
2643
|
+
const setRedirect = /* @__PURE__ */ __name((url) => {
|
|
2644
|
+
setRedirectUrl(url);
|
|
2645
|
+
}, "setRedirect");
|
|
2646
|
+
const getRedirect = /* @__PURE__ */ __name(() => {
|
|
2647
|
+
return getRedirectFromStorage() || redirectUrl;
|
|
2648
|
+
}, "getRedirect");
|
|
2649
|
+
const clearRedirect = /* @__PURE__ */ __name(() => {
|
|
2650
|
+
removeRedirectUrl();
|
|
2651
|
+
clearRedirectFromStorage();
|
|
2652
|
+
}, "clearRedirect");
|
|
2653
|
+
const hasRedirect = /* @__PURE__ */ __name(() => {
|
|
2654
|
+
const stored = getRedirectFromStorage();
|
|
2655
|
+
return stored.length > 0 || redirectUrl.length > 0;
|
|
2656
|
+
}, "hasRedirect");
|
|
2657
|
+
const getFinalRedirectUrl = /* @__PURE__ */ __name(() => {
|
|
2658
|
+
const stored = getRedirectFromStorage();
|
|
2659
|
+
return stored || redirectUrl || fallbackUrl;
|
|
2660
|
+
}, "getFinalRedirectUrl");
|
|
2661
|
+
const useAndClearRedirect = /* @__PURE__ */ __name(() => {
|
|
2662
|
+
const stored = getRedirectFromStorage();
|
|
2663
|
+
const finalUrl = stored || redirectUrl || fallbackUrl;
|
|
2664
|
+
if (clearOnUse) {
|
|
2665
|
+
clearRedirect();
|
|
2666
|
+
}
|
|
2667
|
+
return finalUrl;
|
|
2668
|
+
}, "useAndClearRedirect");
|
|
2669
|
+
return {
|
|
2670
|
+
redirectUrl,
|
|
2671
|
+
setRedirect,
|
|
2672
|
+
getRedirect,
|
|
2673
|
+
clearRedirect,
|
|
2674
|
+
hasRedirect,
|
|
2675
|
+
getFinalRedirectUrl,
|
|
2676
|
+
useAndClearRedirect
|
|
2677
|
+
};
|
|
2678
|
+
}, "useAuthRedirectManager");
|
|
2613
2679
|
|
|
2614
2680
|
// src/auth/hooks/useTwoFactor.ts
|
|
2615
2681
|
var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
2616
2682
|
const { onSuccess, onError, redirectUrl, skipRedirect = false } = options;
|
|
2617
2683
|
const router = useCfgRouter();
|
|
2618
|
-
const [isLoading, setIsLoading] = (0,
|
|
2619
|
-
const [error, setError] = (0,
|
|
2620
|
-
const [warning, setWarning] = (0,
|
|
2621
|
-
const [remainingBackupCodes, setRemainingBackupCodes] = (0,
|
|
2622
|
-
const [attemptsRemaining, setAttemptsRemaining] = (0,
|
|
2623
|
-
const clearError = (0,
|
|
2684
|
+
const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
|
|
2685
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
2686
|
+
const [warning, setWarning] = (0, import_react8.useState)(null);
|
|
2687
|
+
const [remainingBackupCodes, setRemainingBackupCodes] = (0, import_react8.useState)(null);
|
|
2688
|
+
const [attemptsRemaining, setAttemptsRemaining] = (0, import_react8.useState)(null);
|
|
2689
|
+
const clearError = (0, import_react8.useCallback)(() => {
|
|
2624
2690
|
setError(null);
|
|
2625
2691
|
}, []);
|
|
2626
|
-
const handleSuccess = (0,
|
|
2627
|
-
|
|
2628
|
-
CfgAccountsApi.setRefreshToken(response.refresh_token);
|
|
2692
|
+
const handleSuccess = (0, import_react8.useCallback)((response) => {
|
|
2693
|
+
auth.setSession({ access: response.access_token, refresh: response.refresh_token });
|
|
2629
2694
|
if (response.warning) {
|
|
2630
2695
|
setWarning(response.warning);
|
|
2631
2696
|
}
|
|
@@ -2641,12 +2706,12 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
2641
2706
|
}
|
|
2642
2707
|
onSuccess?.(response.user);
|
|
2643
2708
|
if (!skipRedirect) {
|
|
2644
|
-
const finalRedirectUrl = redirectUrl || "/
|
|
2709
|
+
const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || "/";
|
|
2645
2710
|
authLogger.info("2FA successful, redirecting to:", finalRedirectUrl);
|
|
2646
2711
|
router.hardPush(finalRedirectUrl);
|
|
2647
2712
|
}
|
|
2648
2713
|
}, [onSuccess, redirectUrl, router, skipRedirect]);
|
|
2649
|
-
const verifyTOTP = (0,
|
|
2714
|
+
const verifyTOTP = (0, import_react8.useCallback)(async (sessionId, code) => {
|
|
2650
2715
|
if (!sessionId) {
|
|
2651
2716
|
const msg = "Missing 2FA session ID";
|
|
2652
2717
|
setError(msg);
|
|
@@ -2690,7 +2755,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
|
|
|
2690
2755
|
setIsLoading(false);
|
|
2691
2756
|
}
|
|
2692
2757
|
}, [handleSuccess, onError]);
|
|
2693
|
-
const verifyBackupCode = (0,
|
|
2758
|
+
const verifyBackupCode = (0, import_react8.useCallback)(async (sessionId, backupCode) => {
|
|
2694
2759
|
if (!sessionId) {
|
|
2695
2760
|
const msg = "Missing 2FA session ID";
|
|
2696
2761
|
setError(msg);
|
|
@@ -2762,8 +2827,8 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2762
2827
|
} = options;
|
|
2763
2828
|
const formState = useAuthFormState();
|
|
2764
2829
|
const validation = useAuthValidation();
|
|
2765
|
-
const isAutoSubmitFromUrlRef = (0,
|
|
2766
|
-
const processedOtpKeyRef = (0,
|
|
2830
|
+
const isAutoSubmitFromUrlRef = (0, import_react9.useRef)(false);
|
|
2831
|
+
const processedOtpKeyRef = (0, import_react9.useRef)(null);
|
|
2767
2832
|
const {
|
|
2768
2833
|
requestOTP,
|
|
2769
2834
|
verifyOTP,
|
|
@@ -2806,16 +2871,16 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2806
2871
|
// We handle navigation via success step
|
|
2807
2872
|
});
|
|
2808
2873
|
const { validateIdentifier: validateIdentifier2 } = validation;
|
|
2809
|
-
const saveIdentifierToStorage = (0,
|
|
2874
|
+
const saveIdentifierToStorage = (0, import_react9.useCallback)((id) => {
|
|
2810
2875
|
saveEmail(id);
|
|
2811
2876
|
}, [saveEmail]);
|
|
2812
|
-
(0,
|
|
2877
|
+
(0, import_react9.useEffect)(() => {
|
|
2813
2878
|
const savedEmail = getSavedEmail();
|
|
2814
2879
|
if (savedEmail) {
|
|
2815
2880
|
setIdentifier(savedEmail);
|
|
2816
2881
|
}
|
|
2817
2882
|
}, []);
|
|
2818
|
-
const handleIdentifierSubmit = (0,
|
|
2883
|
+
const handleIdentifierSubmit = (0, import_react9.useCallback)(async (e) => {
|
|
2819
2884
|
e.preventDefault();
|
|
2820
2885
|
if (!identifier) {
|
|
2821
2886
|
const msg = "Please enter your email address";
|
|
@@ -2877,7 +2942,7 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2877
2942
|
onError,
|
|
2878
2943
|
sourceUrl
|
|
2879
2944
|
]);
|
|
2880
|
-
const submitOTP = (0,
|
|
2945
|
+
const submitOTP = (0, import_react9.useCallback)(async (submitIdentifier, submitOtp) => {
|
|
2881
2946
|
if (!submitOtp || submitOtp.length < AUTH_CONSTANTS.EMAIL_OTP_LENGTH) {
|
|
2882
2947
|
const msg = `Please enter the ${AUTH_CONSTANTS.EMAIL_OTP_LENGTH}-digit verification code`;
|
|
2883
2948
|
setError(msg);
|
|
@@ -2916,11 +2981,11 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2916
2981
|
setIsLoading(false);
|
|
2917
2982
|
}
|
|
2918
2983
|
}, [verifyOTP, saveIdentifierToStorage, setError, setIsLoading, clearError, onOTPSuccess, onError, sourceUrl, redirectUrl, setTwoFactorSessionId, setShouldPrompt2FA, setStep]);
|
|
2919
|
-
const handleOTPSubmit = (0,
|
|
2984
|
+
const handleOTPSubmit = (0, import_react9.useCallback)(async (e) => {
|
|
2920
2985
|
e.preventDefault();
|
|
2921
2986
|
await submitOTP(identifier, otp);
|
|
2922
2987
|
}, [identifier, otp, submitOTP]);
|
|
2923
|
-
const handleResendOTP = (0,
|
|
2988
|
+
const handleResendOTP = (0, import_react9.useCallback)(async () => {
|
|
2924
2989
|
setIsLoading(true);
|
|
2925
2990
|
clearError();
|
|
2926
2991
|
try {
|
|
@@ -2946,15 +3011,15 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2946
3011
|
setIsLoading(false);
|
|
2947
3012
|
}
|
|
2948
3013
|
}, [identifier, requestOTP, saveIdentifierToStorage, setOtp, setError, setIsLoading, clearError, startRateLimitCountdown, setWebmail, onError, sourceUrl]);
|
|
2949
|
-
const handleBackToIdentifier = (0,
|
|
3014
|
+
const handleBackToIdentifier = (0, import_react9.useCallback)(() => {
|
|
2950
3015
|
setStep("identifier");
|
|
2951
3016
|
clearError();
|
|
2952
3017
|
}, [setStep, clearError]);
|
|
2953
|
-
const forceOTPStep = (0,
|
|
3018
|
+
const forceOTPStep = (0, import_react9.useCallback)(() => {
|
|
2954
3019
|
setStep("otp");
|
|
2955
3020
|
clearError();
|
|
2956
3021
|
}, [setStep, clearError]);
|
|
2957
|
-
const handle2FASubmit = (0,
|
|
3022
|
+
const handle2FASubmit = (0, import_react9.useCallback)(async (e) => {
|
|
2958
3023
|
e.preventDefault();
|
|
2959
3024
|
if (!twoFactorSessionId) {
|
|
2960
3025
|
const msg = "Missing 2FA session";
|
|
@@ -2968,17 +3033,17 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
2968
3033
|
await twoFactor.verifyTOTP(twoFactorSessionId, twoFactorCode);
|
|
2969
3034
|
}
|
|
2970
3035
|
}, [twoFactorSessionId, twoFactorCode, useBackupCode, twoFactor, setError, onError]);
|
|
2971
|
-
const handleUseBackupCode = (0,
|
|
3036
|
+
const handleUseBackupCode = (0, import_react9.useCallback)(() => {
|
|
2972
3037
|
setUseBackupCode(true);
|
|
2973
3038
|
setTwoFactorCode("");
|
|
2974
3039
|
clearError();
|
|
2975
3040
|
}, [setUseBackupCode, setTwoFactorCode, clearError]);
|
|
2976
|
-
const handleUseTOTP = (0,
|
|
3041
|
+
const handleUseTOTP = (0, import_react9.useCallback)(() => {
|
|
2977
3042
|
setUseBackupCode(false);
|
|
2978
3043
|
setTwoFactorCode("");
|
|
2979
3044
|
clearError();
|
|
2980
3045
|
}, [setUseBackupCode, setTwoFactorCode, clearError]);
|
|
2981
|
-
const handleOTPFromUrl = (0,
|
|
3046
|
+
const handleOTPFromUrl = (0, import_react9.useCallback)((detectedOtp, detectedEmail) => {
|
|
2982
3047
|
const autoIdentifier = detectedEmail || getSavedEmail() || "";
|
|
2983
3048
|
if (!autoIdentifier) {
|
|
2984
3049
|
authLogger.warn("No identifier found for auto-submit (no email in URL or storage)");
|
|
@@ -3031,13 +3096,13 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
|
|
|
3031
3096
|
}, "useAuthForm");
|
|
3032
3097
|
|
|
3033
3098
|
// src/auth/hooks/useGithubAuth.ts
|
|
3034
|
-
var
|
|
3099
|
+
var import_react10 = require("react");
|
|
3035
3100
|
var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
3036
3101
|
const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
|
|
3037
3102
|
const router = useCfgRouter();
|
|
3038
|
-
const [isLoading, setIsLoading] = (0,
|
|
3039
|
-
const [error, setError] = (0,
|
|
3040
|
-
const startGithubAuth = (0,
|
|
3103
|
+
const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
|
|
3104
|
+
const [error, setError] = (0, import_react10.useState)(null);
|
|
3105
|
+
const startGithubAuth = (0, import_react10.useCallback)(async () => {
|
|
3041
3106
|
setIsLoading(true);
|
|
3042
3107
|
setError(null);
|
|
3043
3108
|
try {
|
|
@@ -3075,7 +3140,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
|
3075
3140
|
setIsLoading(false);
|
|
3076
3141
|
}
|
|
3077
3142
|
}, [sourceUrl, onError]);
|
|
3078
|
-
const handleGithubCallback = (0,
|
|
3143
|
+
const handleGithubCallback = (0, import_react10.useCallback)(async (code, state) => {
|
|
3079
3144
|
setIsLoading(true);
|
|
3080
3145
|
setError(null);
|
|
3081
3146
|
try {
|
|
@@ -3106,8 +3171,7 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
|
3106
3171
|
throw new Error("Invalid response from OAuth callback");
|
|
3107
3172
|
}
|
|
3108
3173
|
authLogger.info("GitHub OAuth successful, user:", response.user);
|
|
3109
|
-
|
|
3110
|
-
CfgAccountsApi.setRefreshToken(response.refresh);
|
|
3174
|
+
auth.setSession({ access: response.access, refresh: response.refresh });
|
|
3111
3175
|
Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
|
|
3112
3176
|
category: "auth" /* AUTH */,
|
|
3113
3177
|
label: "github"
|
|
@@ -3142,26 +3206,26 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
|
3142
3206
|
}, "useGithubAuth");
|
|
3143
3207
|
|
|
3144
3208
|
// src/auth/hooks/useTwoFactorSetup.ts
|
|
3145
|
-
var
|
|
3209
|
+
var import_react11 = require("react");
|
|
3146
3210
|
var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
|
|
3147
3211
|
const { onComplete, onError } = options;
|
|
3148
|
-
const [isLoading, setIsLoading] = (0,
|
|
3149
|
-
const [error, setError] = (0,
|
|
3150
|
-
const [setupData, setSetupData] = (0,
|
|
3151
|
-
const [backupCodes, setBackupCodes] = (0,
|
|
3152
|
-
const [backupCodesWarning, setBackupCodesWarning] = (0,
|
|
3153
|
-
const [setupStep, setSetupStep] = (0,
|
|
3154
|
-
const clearError = (0,
|
|
3212
|
+
const [isLoading, setIsLoading] = (0, import_react11.useState)(false);
|
|
3213
|
+
const [error, setError] = (0, import_react11.useState)(null);
|
|
3214
|
+
const [setupData, setSetupData] = (0, import_react11.useState)(null);
|
|
3215
|
+
const [backupCodes, setBackupCodes] = (0, import_react11.useState)(null);
|
|
3216
|
+
const [backupCodesWarning, setBackupCodesWarning] = (0, import_react11.useState)(null);
|
|
3217
|
+
const [setupStep, setSetupStep] = (0, import_react11.useState)("idle");
|
|
3218
|
+
const clearError = (0, import_react11.useCallback)(() => {
|
|
3155
3219
|
setError(null);
|
|
3156
3220
|
}, []);
|
|
3157
|
-
const resetSetup = (0,
|
|
3221
|
+
const resetSetup = (0, import_react11.useCallback)(() => {
|
|
3158
3222
|
setSetupData(null);
|
|
3159
3223
|
setBackupCodes(null);
|
|
3160
3224
|
setBackupCodesWarning(null);
|
|
3161
3225
|
setSetupStep("idle");
|
|
3162
3226
|
setError(null);
|
|
3163
3227
|
}, []);
|
|
3164
|
-
const startSetup = (0,
|
|
3228
|
+
const startSetup = (0, import_react11.useCallback)(async (deviceName) => {
|
|
3165
3229
|
setIsLoading(true);
|
|
3166
3230
|
setError(null);
|
|
3167
3231
|
setSetupStep("scanning");
|
|
@@ -3193,7 +3257,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
|
|
|
3193
3257
|
setIsLoading(false);
|
|
3194
3258
|
}
|
|
3195
3259
|
}, [onError]);
|
|
3196
|
-
const confirmSetup = (0,
|
|
3260
|
+
const confirmSetup = (0, import_react11.useCallback)(async (code) => {
|
|
3197
3261
|
if (!setupData) {
|
|
3198
3262
|
const msg = "Setup not started. Call startSetup() first.";
|
|
3199
3263
|
setError(msg);
|
|
@@ -3249,7 +3313,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
|
|
|
3249
3313
|
}, "useTwoFactorSetup");
|
|
3250
3314
|
|
|
3251
3315
|
// src/auth/hooks/useTwoFactorStatus.ts
|
|
3252
|
-
var
|
|
3316
|
+
var import_react12 = require("react");
|
|
3253
3317
|
function extractErrorMessage(err, fallback) {
|
|
3254
3318
|
if (err instanceof APIError) {
|
|
3255
3319
|
const body = err.response;
|
|
@@ -3262,14 +3326,14 @@ function extractErrorMessage(err, fallback) {
|
|
|
3262
3326
|
}
|
|
3263
3327
|
__name(extractErrorMessage, "extractErrorMessage");
|
|
3264
3328
|
var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
|
|
3265
|
-
const [isLoading, setIsLoading] = (0,
|
|
3266
|
-
const [error, setError] = (0,
|
|
3267
|
-
const [has2FAEnabled, setHas2FAEnabled] = (0,
|
|
3268
|
-
const [devices, setDevices] = (0,
|
|
3269
|
-
const clearError = (0,
|
|
3329
|
+
const [isLoading, setIsLoading] = (0, import_react12.useState)(false);
|
|
3330
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
3331
|
+
const [has2FAEnabled, setHas2FAEnabled] = (0, import_react12.useState)(null);
|
|
3332
|
+
const [devices, setDevices] = (0, import_react12.useState)([]);
|
|
3333
|
+
const clearError = (0, import_react12.useCallback)(() => {
|
|
3270
3334
|
setError(null);
|
|
3271
3335
|
}, []);
|
|
3272
|
-
const fetchStatus = (0,
|
|
3336
|
+
const fetchStatus = (0, import_react12.useCallback)(async () => {
|
|
3273
3337
|
setIsLoading(true);
|
|
3274
3338
|
setError(null);
|
|
3275
3339
|
try {
|
|
@@ -3294,259 +3358,39 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
|
|
|
3294
3358
|
setIsLoading(false);
|
|
3295
3359
|
}
|
|
3296
3360
|
}, []);
|
|
3297
|
-
const disable2FA = (0,
|
|
3361
|
+
const disable2FA = (0, import_react12.useCallback)(async (code) => {
|
|
3298
3362
|
if (!code || code.length !== 6) {
|
|
3299
3363
|
setError("Please enter a 6-digit code");
|
|
3300
3364
|
return false;
|
|
3301
3365
|
}
|
|
3302
3366
|
setIsLoading(true);
|
|
3303
3367
|
setError(null);
|
|
3304
|
-
try {
|
|
3305
|
-
authLogger.info("Disabling 2FA...");
|
|
3306
|
-
await CfgTotp.cfgTotpDisableCreate({ body: { code }, throwOnError: true });
|
|
3307
|
-
setHas2FAEnabled(false);
|
|
3308
|
-
setDevices([]);
|
|
3309
|
-
authLogger.info("2FA disabled successfully");
|
|
3310
|
-
return true;
|
|
3311
|
-
} catch (err) {
|
|
3312
|
-
const errorMessage = extractErrorMessage(err, "Invalid verification code");
|
|
3313
|
-
authLogger.error("Failed to disable 2FA:", err);
|
|
3314
|
-
setError(errorMessage);
|
|
3315
|
-
return false;
|
|
3316
|
-
} finally {
|
|
3317
|
-
setIsLoading(false);
|
|
3318
|
-
}
|
|
3319
|
-
}, []);
|
|
3320
|
-
return {
|
|
3321
|
-
isLoading,
|
|
3322
|
-
error,
|
|
3323
|
-
has2FAEnabled,
|
|
3324
|
-
devices,
|
|
3325
|
-
fetchStatus,
|
|
3326
|
-
disable2FA,
|
|
3327
|
-
clearError
|
|
3328
|
-
};
|
|
3329
|
-
}, "useTwoFactorStatus");
|
|
3330
|
-
|
|
3331
|
-
// src/auth/hooks/useSessionStorage.ts
|
|
3332
|
-
var import_react11 = require("react");
|
|
3333
|
-
function useSessionStorage(key, initialValue) {
|
|
3334
|
-
const [storedValue, setStoredValue] = (0, import_react11.useState)(() => {
|
|
3335
|
-
if (typeof window === "undefined") {
|
|
3336
|
-
return initialValue;
|
|
3337
|
-
}
|
|
3338
|
-
try {
|
|
3339
|
-
const item = window.sessionStorage.getItem(key);
|
|
3340
|
-
return item ? JSON.parse(item) : initialValue;
|
|
3341
|
-
} catch (error) {
|
|
3342
|
-
authLogger.error(`Error reading sessionStorage key "${key}":`, error);
|
|
3343
|
-
return initialValue;
|
|
3344
|
-
}
|
|
3345
|
-
});
|
|
3346
|
-
const checkDataSize = /* @__PURE__ */ __name((data) => {
|
|
3347
|
-
try {
|
|
3348
|
-
const jsonString = JSON.stringify(data);
|
|
3349
|
-
const sizeInBytes = new Blob([jsonString]).size;
|
|
3350
|
-
const sizeInKB = sizeInBytes / 1024;
|
|
3351
|
-
if (sizeInKB > 1024) {
|
|
3352
|
-
authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
|
|
3353
|
-
return false;
|
|
3354
|
-
}
|
|
3355
|
-
return true;
|
|
3356
|
-
} catch (error) {
|
|
3357
|
-
authLogger.error(`Error checking data size for key "${key}":`, error);
|
|
3358
|
-
return false;
|
|
3359
|
-
}
|
|
3360
|
-
}, "checkDataSize");
|
|
3361
|
-
const clearOldData = /* @__PURE__ */ __name(() => {
|
|
3362
|
-
try {
|
|
3363
|
-
const keys = Object.keys(sessionStorage).filter((key2) => key2 && typeof key2 === "string");
|
|
3364
|
-
if (keys.length > 50) {
|
|
3365
|
-
const itemsToRemove = Math.ceil(keys.length * 0.2);
|
|
3366
|
-
for (let i = 0; i < itemsToRemove; i++) {
|
|
3367
|
-
try {
|
|
3368
|
-
const key2 = keys[i];
|
|
3369
|
-
if (key2) {
|
|
3370
|
-
sessionStorage.removeItem(key2);
|
|
3371
|
-
sessionStorage.removeItem(`${key2}_timestamp`);
|
|
3372
|
-
}
|
|
3373
|
-
} catch {
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
|
-
} catch (error) {
|
|
3378
|
-
authLogger.error("Error clearing old sessionStorage data:", error);
|
|
3379
|
-
}
|
|
3380
|
-
}, "clearOldData");
|
|
3381
|
-
const forceClearAll = /* @__PURE__ */ __name(() => {
|
|
3382
|
-
try {
|
|
3383
|
-
const keys = Object.keys(sessionStorage);
|
|
3384
|
-
for (const key2 of keys) {
|
|
3385
|
-
try {
|
|
3386
|
-
sessionStorage.removeItem(key2);
|
|
3387
|
-
} catch {
|
|
3388
|
-
}
|
|
3389
|
-
}
|
|
3390
|
-
} catch (error) {
|
|
3391
|
-
authLogger.error("Error force clearing sessionStorage:", error);
|
|
3392
|
-
}
|
|
3393
|
-
}, "forceClearAll");
|
|
3394
|
-
const setValue = /* @__PURE__ */ __name((value) => {
|
|
3395
|
-
try {
|
|
3396
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
3397
|
-
if (!checkDataSize(valueToStore)) {
|
|
3398
|
-
authLogger.warn(`Data size too large for key "${key}", removing key`);
|
|
3399
|
-
try {
|
|
3400
|
-
window.sessionStorage.removeItem(key);
|
|
3401
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
3402
|
-
} catch {
|
|
3403
|
-
}
|
|
3404
|
-
setStoredValue(valueToStore);
|
|
3405
|
-
return;
|
|
3406
|
-
}
|
|
3407
|
-
setStoredValue(valueToStore);
|
|
3408
|
-
if (typeof window !== "undefined") {
|
|
3409
|
-
try {
|
|
3410
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
3411
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
3412
|
-
} catch (storageError) {
|
|
3413
|
-
if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
|
|
3414
|
-
authLogger.warn("sessionStorage quota exceeded, clearing old data...");
|
|
3415
|
-
clearOldData();
|
|
3416
|
-
try {
|
|
3417
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
3418
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
3419
|
-
} catch (retryError) {
|
|
3420
|
-
authLogger.error(`Failed to set sessionStorage key "${key}" after clearing old data:`, retryError);
|
|
3421
|
-
try {
|
|
3422
|
-
forceClearAll();
|
|
3423
|
-
window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
|
|
3424
|
-
window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
|
|
3425
|
-
} catch (finalError) {
|
|
3426
|
-
authLogger.error(`Failed to set sessionStorage key "${key}" after force clearing:`, finalError);
|
|
3427
|
-
setStoredValue(valueToStore);
|
|
3428
|
-
}
|
|
3429
|
-
}
|
|
3430
|
-
} else {
|
|
3431
|
-
throw storageError;
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
} catch (error) {
|
|
3436
|
-
authLogger.error(`Error setting sessionStorage key "${key}":`, error);
|
|
3437
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
3438
|
-
setStoredValue(valueToStore);
|
|
3439
|
-
}
|
|
3440
|
-
}, "setValue");
|
|
3441
|
-
const removeValue = /* @__PURE__ */ __name(() => {
|
|
3442
|
-
try {
|
|
3443
|
-
setStoredValue(initialValue);
|
|
3444
|
-
if (typeof window !== "undefined") {
|
|
3445
|
-
try {
|
|
3446
|
-
window.sessionStorage.removeItem(key);
|
|
3447
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
3448
|
-
} catch (removeError) {
|
|
3449
|
-
if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
|
|
3450
|
-
authLogger.warn("sessionStorage quota exceeded during removal, clearing old data...");
|
|
3451
|
-
clearOldData();
|
|
3452
|
-
try {
|
|
3453
|
-
window.sessionStorage.removeItem(key);
|
|
3454
|
-
window.sessionStorage.removeItem(`${key}_timestamp`);
|
|
3455
|
-
} catch (retryError) {
|
|
3456
|
-
authLogger.error(`Failed to remove sessionStorage key "${key}" after clearing:`, retryError);
|
|
3457
|
-
forceClearAll();
|
|
3458
|
-
}
|
|
3459
|
-
} else {
|
|
3460
|
-
throw removeError;
|
|
3461
|
-
}
|
|
3462
|
-
}
|
|
3463
|
-
}
|
|
3464
|
-
} catch (error) {
|
|
3465
|
-
authLogger.error(`Error removing sessionStorage key "${key}":`, error);
|
|
3466
|
-
}
|
|
3467
|
-
}, "removeValue");
|
|
3468
|
-
return [storedValue, setValue, removeValue];
|
|
3469
|
-
}
|
|
3470
|
-
__name(useSessionStorage, "useSessionStorage");
|
|
3471
|
-
|
|
3472
|
-
// src/auth/hooks/useAuthRedirect.ts
|
|
3473
|
-
var AUTH_REDIRECT_KEY = "auth_redirect_url";
|
|
3474
|
-
var getRedirectFromStorage = /* @__PURE__ */ __name(() => {
|
|
3475
|
-
if (typeof window === "undefined") return "";
|
|
3476
|
-
try {
|
|
3477
|
-
const item = window.sessionStorage.getItem(AUTH_REDIRECT_KEY);
|
|
3478
|
-
return item ? JSON.parse(item) : "";
|
|
3479
|
-
} catch {
|
|
3480
|
-
return "";
|
|
3481
|
-
}
|
|
3482
|
-
}, "getRedirectFromStorage");
|
|
3483
|
-
var clearRedirectFromStorage = /* @__PURE__ */ __name(() => {
|
|
3484
|
-
if (typeof window === "undefined") return;
|
|
3485
|
-
try {
|
|
3486
|
-
window.sessionStorage.removeItem(AUTH_REDIRECT_KEY);
|
|
3487
|
-
window.sessionStorage.removeItem(`${AUTH_REDIRECT_KEY}_timestamp`);
|
|
3488
|
-
} catch {
|
|
3489
|
-
}
|
|
3490
|
-
}, "clearRedirectFromStorage");
|
|
3491
|
-
var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
|
|
3492
|
-
const { fallbackUrl = "/dashboard", clearOnUse = true } = options;
|
|
3493
|
-
const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage(AUTH_REDIRECT_KEY, "");
|
|
3494
|
-
const setRedirect = /* @__PURE__ */ __name((url) => {
|
|
3495
|
-
setRedirectUrl(url);
|
|
3496
|
-
}, "setRedirect");
|
|
3497
|
-
const getRedirect = /* @__PURE__ */ __name(() => {
|
|
3498
|
-
return getRedirectFromStorage() || redirectUrl;
|
|
3499
|
-
}, "getRedirect");
|
|
3500
|
-
const clearRedirect = /* @__PURE__ */ __name(() => {
|
|
3501
|
-
removeRedirectUrl();
|
|
3502
|
-
clearRedirectFromStorage();
|
|
3503
|
-
}, "clearRedirect");
|
|
3504
|
-
const hasRedirect = /* @__PURE__ */ __name(() => {
|
|
3505
|
-
const stored = getRedirectFromStorage();
|
|
3506
|
-
return stored.length > 0 || redirectUrl.length > 0;
|
|
3507
|
-
}, "hasRedirect");
|
|
3508
|
-
const getFinalRedirectUrl = /* @__PURE__ */ __name(() => {
|
|
3509
|
-
const stored = getRedirectFromStorage();
|
|
3510
|
-
return stored || redirectUrl || fallbackUrl;
|
|
3511
|
-
}, "getFinalRedirectUrl");
|
|
3512
|
-
const useAndClearRedirect = /* @__PURE__ */ __name(() => {
|
|
3513
|
-
const stored = getRedirectFromStorage();
|
|
3514
|
-
const finalUrl = stored || redirectUrl || fallbackUrl;
|
|
3515
|
-
if (clearOnUse) {
|
|
3516
|
-
clearRedirect();
|
|
3368
|
+
try {
|
|
3369
|
+
authLogger.info("Disabling 2FA...");
|
|
3370
|
+
await CfgTotp.cfgTotpDisableCreate({ body: { code }, throwOnError: true });
|
|
3371
|
+
setHas2FAEnabled(false);
|
|
3372
|
+
setDevices([]);
|
|
3373
|
+
authLogger.info("2FA disabled successfully");
|
|
3374
|
+
return true;
|
|
3375
|
+
} catch (err) {
|
|
3376
|
+
const errorMessage = extractErrorMessage(err, "Invalid verification code");
|
|
3377
|
+
authLogger.error("Failed to disable 2FA:", err);
|
|
3378
|
+
setError(errorMessage);
|
|
3379
|
+
return false;
|
|
3380
|
+
} finally {
|
|
3381
|
+
setIsLoading(false);
|
|
3517
3382
|
}
|
|
3518
|
-
|
|
3519
|
-
}, "useAndClearRedirect");
|
|
3383
|
+
}, []);
|
|
3520
3384
|
return {
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3385
|
+
isLoading,
|
|
3386
|
+
error,
|
|
3387
|
+
has2FAEnabled,
|
|
3388
|
+
devices,
|
|
3389
|
+
fetchStatus,
|
|
3390
|
+
disable2FA,
|
|
3391
|
+
clearError
|
|
3528
3392
|
};
|
|
3529
|
-
}, "
|
|
3530
|
-
|
|
3531
|
-
// src/auth/hooks/useAuthGuard.ts
|
|
3532
|
-
var import_react12 = require("react");
|
|
3533
|
-
var useAuthGuard = /* @__PURE__ */ __name((options = {}) => {
|
|
3534
|
-
const { redirectTo = "/auth", requireAuth = true, saveRedirectUrl: shouldSaveUrl = true } = options;
|
|
3535
|
-
const { isAuthenticated, isLoading, saveRedirectUrl } = useAuth();
|
|
3536
|
-
const router = useCfgRouter();
|
|
3537
|
-
const [isRedirecting, setIsRedirecting] = (0, import_react12.useState)(false);
|
|
3538
|
-
(0, import_react12.useEffect)(() => {
|
|
3539
|
-
if (!isLoading && requireAuth && !isAuthenticated && !isRedirecting) {
|
|
3540
|
-
if (shouldSaveUrl && typeof window !== "undefined") {
|
|
3541
|
-
const currentUrl = window.location.pathname + window.location.search;
|
|
3542
|
-
saveRedirectUrl(currentUrl);
|
|
3543
|
-
}
|
|
3544
|
-
setIsRedirecting(true);
|
|
3545
|
-
router.push(redirectTo);
|
|
3546
|
-
}
|
|
3547
|
-
}, [isAuthenticated, isLoading, router, redirectTo, requireAuth, isRedirecting, shouldSaveUrl, saveRedirectUrl]);
|
|
3548
|
-
return { isAuthenticated, isLoading, isRedirecting };
|
|
3549
|
-
}, "useAuthGuard");
|
|
3393
|
+
}, "useTwoFactorStatus");
|
|
3550
3394
|
|
|
3551
3395
|
// src/auth/hooks/useLocalStorage.ts
|
|
3552
3396
|
var import_react13 = require("react");
|
|
@@ -3846,150 +3690,16 @@ function getCacheMetadata() {
|
|
|
3846
3690
|
}
|
|
3847
3691
|
__name(getCacheMetadata, "getCacheMetadata");
|
|
3848
3692
|
|
|
3849
|
-
// src/auth/hooks/useTokenRefresh.ts
|
|
3850
|
-
var import_react14 = require("react");
|
|
3851
|
-
|
|
3852
|
-
// src/auth/refreshHandler.ts
|
|
3853
|
-
var _registered = false;
|
|
3854
|
-
function ensureRefreshHandler() {
|
|
3855
|
-
if (_registered) return;
|
|
3856
|
-
_registered = true;
|
|
3857
|
-
CfgAccountsApi.setRefreshHandler(async (refresh) => {
|
|
3858
|
-
const result = await CfgAccountsAuth.cfgAccountsTokenRefreshCreate({
|
|
3859
|
-
body: { refresh },
|
|
3860
|
-
throwOnError: true
|
|
3861
|
-
});
|
|
3862
|
-
const access = result.data?.access;
|
|
3863
|
-
if (!access) {
|
|
3864
|
-
authLogger.error("Refresh response missing access token");
|
|
3865
|
-
return null;
|
|
3866
|
-
}
|
|
3867
|
-
return { access, refresh: result.data?.refresh ?? refresh };
|
|
3868
|
-
});
|
|
3869
|
-
authLogger.debug("Canonical refresh handler registered");
|
|
3870
|
-
}
|
|
3871
|
-
__name(ensureRefreshHandler, "ensureRefreshHandler");
|
|
3872
|
-
function triggerRefresh() {
|
|
3873
|
-
ensureRefreshHandler();
|
|
3874
|
-
const maybe = CfgAccountsApi;
|
|
3875
|
-
if (typeof maybe.refreshNow === "function") {
|
|
3876
|
-
return maybe.refreshNow();
|
|
3877
|
-
}
|
|
3878
|
-
authLogger.warn("api.refreshNow() unavailable \u2014 run `make generate` to regenerate the client");
|
|
3879
|
-
return Promise.resolve(null);
|
|
3880
|
-
}
|
|
3881
|
-
__name(triggerRefresh, "triggerRefresh");
|
|
3882
|
-
|
|
3883
|
-
// src/auth/utils/jwt.ts
|
|
3884
|
-
function decodeSegment(segment) {
|
|
3885
|
-
try {
|
|
3886
|
-
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
3887
|
-
if (typeof atob !== "function") return null;
|
|
3888
|
-
const json = atob(base64);
|
|
3889
|
-
return JSON.parse(json);
|
|
3890
|
-
} catch {
|
|
3891
|
-
return null;
|
|
3892
|
-
}
|
|
3893
|
-
}
|
|
3894
|
-
__name(decodeSegment, "decodeSegment");
|
|
3895
|
-
function getTokenExpiry(token) {
|
|
3896
|
-
if (!token || typeof token !== "string") return null;
|
|
3897
|
-
const parts = token.split(".");
|
|
3898
|
-
if (parts.length !== 3) return null;
|
|
3899
|
-
const payload = decodeSegment(parts[1]);
|
|
3900
|
-
if (!payload || typeof payload !== "object") return null;
|
|
3901
|
-
const exp = payload.exp;
|
|
3902
|
-
if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
|
|
3903
|
-
return exp * 1e3;
|
|
3904
|
-
}
|
|
3905
|
-
__name(getTokenExpiry, "getTokenExpiry");
|
|
3906
|
-
function isTokenExpired(token, skewMs = 0, now = Date.now()) {
|
|
3907
|
-
const expiry = getTokenExpiry(token);
|
|
3908
|
-
if (expiry === null) return true;
|
|
3909
|
-
return expiry - skewMs <= now;
|
|
3910
|
-
}
|
|
3911
|
-
__name(isTokenExpired, "isTokenExpired");
|
|
3912
|
-
function isTokenExpiringSoon(token, thresholdMs, now = Date.now()) {
|
|
3913
|
-
const expiry = getTokenExpiry(token);
|
|
3914
|
-
if (expiry === null) return false;
|
|
3915
|
-
return expiry - now < thresholdMs;
|
|
3916
|
-
}
|
|
3917
|
-
__name(isTokenExpiringSoon, "isTokenExpiringSoon");
|
|
3918
|
-
|
|
3919
|
-
// src/auth/hooks/useTokenRefresh.ts
|
|
3920
|
-
var TOKEN_REFRESH_THRESHOLD_MS = 10 * 60 * 1e3;
|
|
3921
|
-
var CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3922
|
-
function useTokenRefresh(options = {}) {
|
|
3923
|
-
const { enabled = true, onRefresh, onRefreshError } = options;
|
|
3924
|
-
const refreshToken = (0, import_react14.useCallback)(async () => {
|
|
3925
|
-
if (!CfgAccountsApi.getRefreshToken()) {
|
|
3926
|
-
authLogger.warn("No refresh token available");
|
|
3927
|
-
return false;
|
|
3928
|
-
}
|
|
3929
|
-
authLogger.info("Refreshing token...");
|
|
3930
|
-
try {
|
|
3931
|
-
const newAccessToken = await triggerRefresh();
|
|
3932
|
-
if (!newAccessToken) {
|
|
3933
|
-
throw new Error("Token refresh failed");
|
|
3934
|
-
}
|
|
3935
|
-
authLogger.info("Token refreshed successfully");
|
|
3936
|
-
onRefresh?.(newAccessToken);
|
|
3937
|
-
return true;
|
|
3938
|
-
} catch (error) {
|
|
3939
|
-
authLogger.error("Token refresh error:", error);
|
|
3940
|
-
onRefreshError?.(error instanceof Error ? error : new Error(String(error)));
|
|
3941
|
-
return false;
|
|
3942
|
-
}
|
|
3943
|
-
}, [onRefresh, onRefreshError]);
|
|
3944
|
-
const checkAndRefresh = (0, import_react14.useCallback)(async () => {
|
|
3945
|
-
const token = CfgAccountsApi.getToken();
|
|
3946
|
-
if (!token) return;
|
|
3947
|
-
if (isTokenExpiringSoon(token, TOKEN_REFRESH_THRESHOLD_MS)) {
|
|
3948
|
-
authLogger.info("Token expiring soon, refreshing proactively");
|
|
3949
|
-
await refreshToken();
|
|
3950
|
-
}
|
|
3951
|
-
}, [refreshToken]);
|
|
3952
|
-
(0, import_react14.useEffect)(() => {
|
|
3953
|
-
if (!enabled) return;
|
|
3954
|
-
checkAndRefresh();
|
|
3955
|
-
const intervalId = setInterval(checkAndRefresh, CHECK_INTERVAL_MS);
|
|
3956
|
-
return () => clearInterval(intervalId);
|
|
3957
|
-
}, [enabled, checkAndRefresh]);
|
|
3958
|
-
(0, import_react14.useEffect)(() => {
|
|
3959
|
-
if (!enabled) return;
|
|
3960
|
-
const handleFocus = /* @__PURE__ */ __name(() => {
|
|
3961
|
-
authLogger.debug("Window focused, checking token...");
|
|
3962
|
-
checkAndRefresh();
|
|
3963
|
-
}, "handleFocus");
|
|
3964
|
-
window.addEventListener("focus", handleFocus);
|
|
3965
|
-
return () => window.removeEventListener("focus", handleFocus);
|
|
3966
|
-
}, [enabled, checkAndRefresh]);
|
|
3967
|
-
(0, import_react14.useEffect)(() => {
|
|
3968
|
-
if (!enabled) return;
|
|
3969
|
-
const handleOnline = /* @__PURE__ */ __name(() => {
|
|
3970
|
-
authLogger.info("Network reconnected, checking token...");
|
|
3971
|
-
checkAndRefresh();
|
|
3972
|
-
}, "handleOnline");
|
|
3973
|
-
window.addEventListener("online", handleOnline);
|
|
3974
|
-
return () => window.removeEventListener("online", handleOnline);
|
|
3975
|
-
}, [enabled, checkAndRefresh]);
|
|
3976
|
-
return {
|
|
3977
|
-
refreshToken,
|
|
3978
|
-
checkAndRefresh
|
|
3979
|
-
};
|
|
3980
|
-
}
|
|
3981
|
-
__name(useTokenRefresh, "useTokenRefresh");
|
|
3982
|
-
|
|
3983
3693
|
// src/auth/hooks/useDeleteAccount.ts
|
|
3984
|
-
var
|
|
3694
|
+
var import_react14 = require("react");
|
|
3985
3695
|
var useDeleteAccount = /* @__PURE__ */ __name(() => {
|
|
3986
|
-
const [isLoading, setIsLoading] = (0,
|
|
3987
|
-
const [error, setError] = (0,
|
|
3696
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
|
|
3697
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
3988
3698
|
const { logout } = useAuth();
|
|
3989
|
-
const clearError = (0,
|
|
3699
|
+
const clearError = (0, import_react14.useCallback)(() => {
|
|
3990
3700
|
setError(null);
|
|
3991
3701
|
}, []);
|
|
3992
|
-
const deleteAccount = (0,
|
|
3702
|
+
const deleteAccount = (0, import_react14.useCallback)(async () => {
|
|
3993
3703
|
setIsLoading(true);
|
|
3994
3704
|
setError(null);
|
|
3995
3705
|
try {
|
|
@@ -4018,32 +3728,8 @@ var useDeleteAccount = /* @__PURE__ */ __name(() => {
|
|
|
4018
3728
|
};
|
|
4019
3729
|
}, "useDeleteAccount");
|
|
4020
3730
|
|
|
4021
|
-
// src/auth/utils/guard.ts
|
|
4022
|
-
function resolveGuardIsLoading(s) {
|
|
4023
|
-
if (!s.mounted) return true;
|
|
4024
|
-
if (!s.requireAuth) return false;
|
|
4025
|
-
return s.authLoading || s.isRedirecting || !s.isAuthenticated;
|
|
4026
|
-
}
|
|
4027
|
-
__name(resolveGuardIsLoading, "resolveGuardIsLoading");
|
|
4028
|
-
function shouldRedirectToAuth(s) {
|
|
4029
|
-
return s.mounted && s.requireAuth && !s.authLoading && !s.isAuthenticated && !s.isRedirecting;
|
|
4030
|
-
}
|
|
4031
|
-
__name(shouldRedirectToAuth, "shouldRedirectToAuth");
|
|
4032
|
-
function resolveGuardIsAuthenticated(s) {
|
|
4033
|
-
return !s.requireAuth || s.isAuthenticated;
|
|
4034
|
-
}
|
|
4035
|
-
__name(resolveGuardIsAuthenticated, "resolveGuardIsAuthenticated");
|
|
4036
|
-
function nextAuthEvaluationDelay(accessExp, refreshExp, now) {
|
|
4037
|
-
const future = [accessExp, refreshExp].filter(
|
|
4038
|
-
(d) => d !== null && d > now
|
|
4039
|
-
);
|
|
4040
|
-
if (future.length === 0) return null;
|
|
4041
|
-
return Math.max(0, Math.min(...future) - now) + 1e3;
|
|
4042
|
-
}
|
|
4043
|
-
__name(nextAuthEvaluationDelay, "nextAuthEvaluationDelay");
|
|
4044
|
-
|
|
4045
3731
|
// src/auth/context/AccountsContext.tsx
|
|
4046
|
-
var
|
|
3732
|
+
var import_react15 = require("react");
|
|
4047
3733
|
|
|
4048
3734
|
// src/_api/generated/_cfg_accounts/hooks/useCfgAccountsApiKeyRegenerateCreate.ts
|
|
4049
3735
|
var import_mutation = __toESM(require("swr/mutation"), 1);
|
|
@@ -4495,9 +4181,12 @@ function useCfgAccountsProfileUpdateUpdate(config) {
|
|
|
4495
4181
|
}
|
|
4496
4182
|
__name(useCfgAccountsProfileUpdateUpdate, "useCfgAccountsProfileUpdateUpdate");
|
|
4497
4183
|
|
|
4498
|
-
// src/_api/generated/_cfg_accounts/hooks/
|
|
4184
|
+
// src/_api/generated/_cfg_accounts/hooks/useCfgAccountsTokenBlacklistCreate.ts
|
|
4499
4185
|
var import_mutation15 = __toESM(require("swr/mutation"), 1);
|
|
4500
4186
|
|
|
4187
|
+
// src/_api/generated/_cfg_accounts/hooks/useCfgAccountsTokenRefreshCreate.ts
|
|
4188
|
+
var import_mutation16 = __toESM(require("swr/mutation"), 1);
|
|
4189
|
+
|
|
4501
4190
|
// src/_api/generated/_cfg_accounts/schemas/TokenRefresh.ts
|
|
4502
4191
|
var import_zod19 = require("zod");
|
|
4503
4192
|
var TokenRefreshSchema = import_zod19.z.object({
|
|
@@ -4505,53 +4194,6 @@ var TokenRefreshSchema = import_zod19.z.object({
|
|
|
4505
4194
|
refresh: import_zod19.z.string()
|
|
4506
4195
|
});
|
|
4507
4196
|
|
|
4508
|
-
// src/_api/generated/_cfg_accounts/hooks/useCfgAccountsTokenRefreshCreate.ts
|
|
4509
|
-
function useCfgAccountsTokenRefreshCreate(config) {
|
|
4510
|
-
return (0, import_mutation15.default)(
|
|
4511
|
-
["cfg_accounts_token_refresh_create"],
|
|
4512
|
-
async (_key, { arg }) => {
|
|
4513
|
-
const res = await CfgAccountsAuth.cfgAccountsTokenRefreshCreate({ ...arg, throwOnError: true });
|
|
4514
|
-
const data = res.data;
|
|
4515
|
-
const parsed = TokenRefreshSchema.safeParse(data);
|
|
4516
|
-
if (!parsed.success) {
|
|
4517
|
-
console.warn(
|
|
4518
|
-
"[zod] response did not match schema",
|
|
4519
|
-
{
|
|
4520
|
-
operation: "cfg_accounts_token_refresh_create",
|
|
4521
|
-
method: "POST",
|
|
4522
|
-
path: "/cfg/accounts/token/refresh/",
|
|
4523
|
-
issues: parsed.error.issues,
|
|
4524
|
-
data
|
|
4525
|
-
}
|
|
4526
|
-
);
|
|
4527
|
-
if (typeof window !== "undefined") {
|
|
4528
|
-
try {
|
|
4529
|
-
window.dispatchEvent(
|
|
4530
|
-
new CustomEvent("zod-validation-error", {
|
|
4531
|
-
detail: {
|
|
4532
|
-
operation: "cfg_accounts_token_refresh_create",
|
|
4533
|
-
method: "POST",
|
|
4534
|
-
path: "/cfg/accounts/token/refresh/",
|
|
4535
|
-
issues: parsed.error.issues,
|
|
4536
|
-
data,
|
|
4537
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
4538
|
-
},
|
|
4539
|
-
bubbles: true,
|
|
4540
|
-
cancelable: false
|
|
4541
|
-
})
|
|
4542
|
-
);
|
|
4543
|
-
} catch {
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
return data;
|
|
4547
|
-
}
|
|
4548
|
-
return parsed.data;
|
|
4549
|
-
},
|
|
4550
|
-
config
|
|
4551
|
-
);
|
|
4552
|
-
}
|
|
4553
|
-
__name(useCfgAccountsTokenRefreshCreate, "useCfgAccountsTokenRefreshCreate");
|
|
4554
|
-
|
|
4555
4197
|
// src/_api/generated/_cfg_accounts/schemas/APIKeyRequest.ts
|
|
4556
4198
|
var import_zod20 = require("zod");
|
|
4557
4199
|
var APIKeyRequestSchema = import_zod20.z.object({
|
|
@@ -4641,28 +4283,34 @@ var PatchedCfgUserUpdateRequestSchema = import_zod30.z.object({
|
|
|
4641
4283
|
timezone: import_zod30.z.string().max(64).optional()
|
|
4642
4284
|
});
|
|
4643
4285
|
|
|
4644
|
-
// src/_api/generated/_cfg_accounts/schemas/
|
|
4286
|
+
// src/_api/generated/_cfg_accounts/schemas/TokenBlacklistRequest.ts
|
|
4645
4287
|
var import_zod31 = require("zod");
|
|
4646
|
-
var
|
|
4288
|
+
var TokenBlacklistRequestSchema = import_zod31.z.object({
|
|
4647
4289
|
refresh: import_zod31.z.string().min(1)
|
|
4648
4290
|
});
|
|
4649
4291
|
|
|
4292
|
+
// src/_api/generated/_cfg_accounts/schemas/TokenRefreshRequest.ts
|
|
4293
|
+
var import_zod32 = require("zod");
|
|
4294
|
+
var TokenRefreshRequestSchema = import_zod32.z.object({
|
|
4295
|
+
refresh: import_zod32.z.string().min(1)
|
|
4296
|
+
});
|
|
4297
|
+
|
|
4650
4298
|
// src/auth/context/AccountsContext.tsx
|
|
4651
4299
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
4652
|
-
var AccountsContext = (0,
|
|
4300
|
+
var AccountsContext = (0, import_react15.createContext)(void 0);
|
|
4653
4301
|
function AccountsProvider({ children }) {
|
|
4654
|
-
const [profile, setProfile] = (0,
|
|
4302
|
+
const [profile, setProfile] = (0, import_react15.useState)(() => {
|
|
4655
4303
|
const cached = getCachedProfile();
|
|
4656
4304
|
return cached || void 0;
|
|
4657
4305
|
});
|
|
4658
|
-
const [isLoadingProfile, setIsLoadingProfile] = (0,
|
|
4659
|
-
const [profileError, setProfileError] = (0,
|
|
4660
|
-
const profileRef = (0,
|
|
4661
|
-
const isLoadingRef = (0,
|
|
4662
|
-
(0,
|
|
4306
|
+
const [isLoadingProfile, setIsLoadingProfile] = (0, import_react15.useState)(false);
|
|
4307
|
+
const [profileError, setProfileError] = (0, import_react15.useState)(null);
|
|
4308
|
+
const profileRef = (0, import_react15.useRef)(profile);
|
|
4309
|
+
const isLoadingRef = (0, import_react15.useRef)(false);
|
|
4310
|
+
(0, import_react15.useEffect)(() => {
|
|
4663
4311
|
profileRef.current = profile;
|
|
4664
4312
|
}, [profile]);
|
|
4665
|
-
(0,
|
|
4313
|
+
(0, import_react15.useEffect)(() => {
|
|
4666
4314
|
isLoadingRef.current = isLoadingProfile;
|
|
4667
4315
|
}, [isLoadingProfile]);
|
|
4668
4316
|
const { trigger: triggerProfileUpdate } = useCfgAccountsProfileUpdateUpdate();
|
|
@@ -4670,8 +4318,7 @@ function AccountsProvider({ children }) {
|
|
|
4670
4318
|
const { trigger: triggerAvatar } = useCfgAccountsProfileAvatarCreate();
|
|
4671
4319
|
const { trigger: triggerOtpRequest } = useCfgAccountsOtpRequestCreate();
|
|
4672
4320
|
const { trigger: triggerOtpVerify } = useCfgAccountsOtpVerifyCreate();
|
|
4673
|
-
const
|
|
4674
|
-
const refreshProfile = (0, import_react16.useCallback)(async (options) => {
|
|
4321
|
+
const refreshProfile = (0, import_react15.useCallback)(async (options) => {
|
|
4675
4322
|
const { callerId, force } = options || {};
|
|
4676
4323
|
if (isLoadingRef.current) {
|
|
4677
4324
|
authLogger.debug(`Profile loading in progress, skipping (caller: ${callerId})`);
|
|
@@ -4723,8 +4370,7 @@ function AccountsProvider({ children }) {
|
|
|
4723
4370
|
return result;
|
|
4724
4371
|
}
|
|
4725
4372
|
if (result.access && result.refresh) {
|
|
4726
|
-
|
|
4727
|
-
CfgAccountsApi.setRefreshToken(result.refresh);
|
|
4373
|
+
auth.setSession({ access: result.access, refresh: result.refresh });
|
|
4728
4374
|
try {
|
|
4729
4375
|
await refreshProfile({ callerId: "verifyOTP", force: true });
|
|
4730
4376
|
} catch (profileError2) {
|
|
@@ -4733,22 +4379,19 @@ function AccountsProvider({ children }) {
|
|
|
4733
4379
|
}
|
|
4734
4380
|
return result;
|
|
4735
4381
|
}, "verifyOTP");
|
|
4736
|
-
const
|
|
4737
|
-
const
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4382
|
+
const logout = (0, import_react15.useCallback)(() => {
|
|
4383
|
+
const refresh = auth.getRefreshToken();
|
|
4384
|
+
if (refresh) {
|
|
4385
|
+
CfgAccountsAuth.cfgAccountsTokenBlacklistCreate({ body: { refresh } }).catch(() => {
|
|
4386
|
+
authLogger.warn("Refresh-token blacklist failed (logged out locally anyway)");
|
|
4387
|
+
});
|
|
4742
4388
|
}
|
|
4743
|
-
|
|
4744
|
-
}, "refreshToken");
|
|
4745
|
-
const logout = (0, import_react16.useCallback)(() => {
|
|
4746
|
-
CfgAccountsApi.clearToken();
|
|
4389
|
+
auth.clearSession();
|
|
4747
4390
|
setProfile(void 0);
|
|
4748
4391
|
setProfileError(null);
|
|
4749
4392
|
clearProfileCache();
|
|
4750
4393
|
}, []);
|
|
4751
|
-
const value = (0,
|
|
4394
|
+
const value = (0, import_react15.useMemo)(() => ({
|
|
4752
4395
|
profile,
|
|
4753
4396
|
isLoadingProfile,
|
|
4754
4397
|
profileError,
|
|
@@ -4758,7 +4401,6 @@ function AccountsProvider({ children }) {
|
|
|
4758
4401
|
refreshProfile,
|
|
4759
4402
|
requestOTP,
|
|
4760
4403
|
verifyOTP,
|
|
4761
|
-
refreshToken,
|
|
4762
4404
|
logout
|
|
4763
4405
|
}), [
|
|
4764
4406
|
profile,
|
|
@@ -4770,14 +4412,13 @@ function AccountsProvider({ children }) {
|
|
|
4770
4412
|
refreshProfile,
|
|
4771
4413
|
requestOTP,
|
|
4772
4414
|
verifyOTP,
|
|
4773
|
-
refreshToken,
|
|
4774
4415
|
logout
|
|
4775
4416
|
]);
|
|
4776
4417
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccountsContext.Provider, { value, children });
|
|
4777
4418
|
}
|
|
4778
4419
|
__name(AccountsProvider, "AccountsProvider");
|
|
4779
4420
|
function useAccountsContext() {
|
|
4780
|
-
const context = (0,
|
|
4421
|
+
const context = (0, import_react15.useContext)(AccountsContext);
|
|
4781
4422
|
if (!context) {
|
|
4782
4423
|
throw new Error("useAccountsContext must be used within AccountsProvider");
|
|
4783
4424
|
}
|
|
@@ -4789,281 +4430,127 @@ __name(useAccountsContext, "useAccountsContext");
|
|
|
4789
4430
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
4790
4431
|
var defaultRoutes = {
|
|
4791
4432
|
auth: "/auth",
|
|
4792
|
-
defaultCallback: "/
|
|
4433
|
+
defaultCallback: "/",
|
|
4793
4434
|
defaultAuthCallback: "/auth"
|
|
4794
4435
|
};
|
|
4795
|
-
var
|
|
4436
|
+
var resolveAuthRoutes = /* @__PURE__ */ __name((routes) => ({
|
|
4437
|
+
auth: routes?.auth || defaultRoutes.auth,
|
|
4438
|
+
defaultCallback: routes?.defaultCallback || defaultRoutes.defaultCallback,
|
|
4439
|
+
defaultAuthCallback: routes?.defaultAuthCallback || routes?.auth || defaultRoutes.defaultAuthCallback
|
|
4440
|
+
}), "resolveAuthRoutes");
|
|
4796
4441
|
var EMAIL_STORAGE_KEY = "auth_email";
|
|
4797
|
-
var
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
const accessDead = isTokenExpired(accessToken, 0, now);
|
|
4803
|
-
if (!accessDead) return false;
|
|
4804
|
-
const refreshDead = isTokenExpired(refreshToken, 0, now);
|
|
4805
|
-
return refreshDead;
|
|
4806
|
-
}, "isSessionDeadOnBootstrap");
|
|
4807
|
-
var isSessionAlive = /* @__PURE__ */ __name((now = Date.now()) => {
|
|
4808
|
-
if (typeof window === "undefined") return false;
|
|
4809
|
-
return !isSessionDeadOnBootstrap(
|
|
4810
|
-
CfgAccountsApi.getToken(),
|
|
4811
|
-
CfgAccountsApi.getRefreshToken(),
|
|
4812
|
-
now
|
|
4813
|
-
);
|
|
4814
|
-
}, "isSessionAlive");
|
|
4442
|
+
var AuthContext = (0, import_react16.createContext)(void 0);
|
|
4443
|
+
var apiErrorMessage = /* @__PURE__ */ __name((error) => {
|
|
4444
|
+
const body = error.response;
|
|
4445
|
+
return body?.error || body?.detail || body?.message || error.errorMessage;
|
|
4446
|
+
}, "apiErrorMessage");
|
|
4815
4447
|
var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
4816
4448
|
const accounts = useAccountsContext();
|
|
4817
|
-
|
|
4818
|
-
const
|
|
4819
|
-
fallbackUrl: config?.routes?.defaultCallback || defaultRoutes.defaultCallback,
|
|
4820
|
-
clearOnUse: true
|
|
4821
|
-
});
|
|
4822
|
-
const [isLoading, setIsLoading] = (0, import_react17.useState)(() => {
|
|
4823
|
-
if (typeof window !== "undefined") {
|
|
4824
|
-
const hasTokens = hasValidTokens();
|
|
4825
|
-
return !hasTokens;
|
|
4826
|
-
}
|
|
4827
|
-
return true;
|
|
4828
|
-
});
|
|
4829
|
-
const [initialized, setInitialized] = (0, import_react17.useState)(false);
|
|
4830
|
-
const [authTick, setAuthTick] = (0, import_react17.useState)(0);
|
|
4831
|
-
const bumpAuthTick = (0, import_react17.useCallback)(() => setAuthTick((t) => t + 1), []);
|
|
4832
|
-
(0, import_react17.useEffect)(() => {
|
|
4833
|
-
if (typeof window === "undefined") return;
|
|
4834
|
-
const delay = nextAuthEvaluationDelay(
|
|
4835
|
-
getTokenExpiry(CfgAccountsApi.getToken()),
|
|
4836
|
-
getTokenExpiry(CfgAccountsApi.getRefreshToken()),
|
|
4837
|
-
Date.now()
|
|
4838
|
-
);
|
|
4839
|
-
if (delay === null) return;
|
|
4840
|
-
const timer = setTimeout(bumpAuthTick, Math.min(delay, 2e9));
|
|
4841
|
-
return () => clearTimeout(timer);
|
|
4842
|
-
}, [authTick, bumpAuthTick]);
|
|
4843
|
-
const onUnauthorizedRef = (0, import_react17.useRef)(false);
|
|
4449
|
+
const session = useSession();
|
|
4450
|
+
const isAuthenticated = session.status === "authenticated";
|
|
4844
4451
|
const router = useCfgRouter();
|
|
4845
4452
|
const pathname = (0, import_navigation4.usePathname)();
|
|
4846
4453
|
const queryParams = useQueryParams();
|
|
4847
|
-
const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage(
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
const
|
|
4852
|
-
(0,
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4454
|
+
const [storedEmail, setStoredEmail, clearStoredEmail] = useLocalStorage(
|
|
4455
|
+
EMAIL_STORAGE_KEY,
|
|
4456
|
+
null
|
|
4457
|
+
);
|
|
4458
|
+
const routes = (0, import_react16.useMemo)(() => resolveAuthRoutes(config?.routes), [config?.routes]);
|
|
4459
|
+
const routesRef = (0, import_react16.useRef)(routes);
|
|
4460
|
+
(0, import_react16.useEffect)(() => {
|
|
4461
|
+
routesRef.current = routes;
|
|
4462
|
+
}, [routes]);
|
|
4463
|
+
const redirectManager = useAuthRedirectManager({
|
|
4464
|
+
fallbackUrl: routes.defaultCallback,
|
|
4465
|
+
clearOnUse: true
|
|
4466
|
+
});
|
|
4467
|
+
const configRef = (0, import_react16.useRef)(config);
|
|
4468
|
+
(0, import_react16.useEffect)(() => {
|
|
4856
4469
|
configRef.current = config;
|
|
4857
4470
|
}, [config]);
|
|
4858
|
-
const
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
authLogger.info("Token auto-refreshed successfully");
|
|
4878
|
-
}, "onRefresh"),
|
|
4879
|
-
onRefreshError: handleProactiveRefreshError
|
|
4880
|
-
});
|
|
4881
|
-
const handleGlobalAuthError = (0, import_react17.useCallback)((error, context = "API Request") => {
|
|
4882
|
-
const isAuthError = error?.status === 401 || error?.statusCode === 401 || error?.code === "token_not_valid" || error?.code === "authentication_failed";
|
|
4883
|
-
if (isAuthError) {
|
|
4884
|
-
authLogger.warn(`Authentication error in ${context}, clearing tokens`);
|
|
4885
|
-
clearAuthState(`globalAuthError:${context}`);
|
|
4886
|
-
return true;
|
|
4471
|
+
const accountsRef = (0, import_react16.useRef)(accounts);
|
|
4472
|
+
(0, import_react16.useEffect)(() => {
|
|
4473
|
+
accountsRef.current = accounts;
|
|
4474
|
+
}, [accounts]);
|
|
4475
|
+
const pendingManualNavRef = (0, import_react16.useRef)(false);
|
|
4476
|
+
const [profileSettled, setProfileSettled] = (0, import_react16.useState)(false);
|
|
4477
|
+
const hasProfile = Boolean(accounts.profile);
|
|
4478
|
+
const profileAttemptedRef = (0, import_react16.useRef)(false);
|
|
4479
|
+
(0, import_react16.useEffect)(() => {
|
|
4480
|
+
if (!isAuthenticated) {
|
|
4481
|
+
profileAttemptedRef.current = false;
|
|
4482
|
+
const hasTokens = Boolean(auth.getToken() || auth.getRefreshToken());
|
|
4483
|
+
if (hasTokens && !auth.isAuthenticated()) {
|
|
4484
|
+
authLogger.warn("Dead session in storage \u2014 clearing");
|
|
4485
|
+
auth.clearSession();
|
|
4486
|
+
clearProfileCache();
|
|
4487
|
+
}
|
|
4488
|
+
setProfileSettled(true);
|
|
4489
|
+
return;
|
|
4887
4490
|
}
|
|
4888
|
-
if (
|
|
4889
|
-
|
|
4491
|
+
if (hasProfile || profileAttemptedRef.current) {
|
|
4492
|
+
setProfileSettled(true);
|
|
4493
|
+
return;
|
|
4890
4494
|
}
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
clearAuthState("onUnauthorized:401");
|
|
4899
|
-
const authCallbackUrl = configRef.current?.routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback;
|
|
4900
|
-
router.hardReplace(authCallbackUrl);
|
|
4901
|
-
}, "handler");
|
|
4902
|
-
CfgAccountsApi.onUnauthorized(handler);
|
|
4495
|
+
profileAttemptedRef.current = true;
|
|
4496
|
+
let cancelled = false;
|
|
4497
|
+
accountsRef.current.refreshProfile({ callerId: "AuthProvider:bootstrap" }).catch((error) => {
|
|
4498
|
+
authLogger.warn("Profile load failed (session kept):", error);
|
|
4499
|
+
}).finally(() => {
|
|
4500
|
+
if (!cancelled) setProfileSettled(true);
|
|
4501
|
+
});
|
|
4903
4502
|
return () => {
|
|
4904
|
-
|
|
4503
|
+
cancelled = true;
|
|
4905
4504
|
};
|
|
4906
|
-
}, [
|
|
4907
|
-
const
|
|
4908
|
-
const
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
const isAuth = CfgAccountsApi.isAuthenticated();
|
|
4929
|
-
const token = CfgAccountsApi.getToken();
|
|
4930
|
-
if (!isAuth) {
|
|
4931
|
-
authLogger.warn("No valid authentication token, throwing error");
|
|
4932
|
-
throw new Error("No valid authentication token");
|
|
4933
|
-
}
|
|
4934
|
-
if (accounts.profile && !accounts.isLoadingProfile) {
|
|
4935
|
-
authLogger.debug("Profile already loaded in AccountsContext, skipping API call");
|
|
4936
|
-
setInitialized(true);
|
|
4937
|
-
return;
|
|
4938
|
-
}
|
|
4939
|
-
const refreshedProfile = await accounts.refreshProfile({ callerId: finalCallerId });
|
|
4940
|
-
if (refreshedProfile) {
|
|
4941
|
-
authLogger.info("Profile loaded successfully:", refreshedProfile.id);
|
|
4942
|
-
} else {
|
|
4943
|
-
authLogger.warn("Profile refresh returned undefined - but keeping tokens");
|
|
4944
|
-
}
|
|
4945
|
-
setInitialized(true);
|
|
4946
|
-
} catch (error) {
|
|
4947
|
-
authLogger.error("Failed to load profile:", error);
|
|
4948
|
-
const isAuthError = error?.status === 401 || error?.statusCode === 401 || error?.code === "token_not_valid" || error?.code === "authentication_failed";
|
|
4949
|
-
if (isAuthError) {
|
|
4950
|
-
authLogger.warn("Authentication error, clearing session");
|
|
4951
|
-
clearAuthState("loadCurrentProfile:authError");
|
|
4952
|
-
} else {
|
|
4953
|
-
authLogger.warn("Profile load failed but keeping session (non-auth error)");
|
|
4954
|
-
setInitialized(true);
|
|
4955
|
-
}
|
|
4956
|
-
} finally {
|
|
4957
|
-
isLoadingProfileRef.current = false;
|
|
4505
|
+
}, [isAuthenticated, hasProfile]);
|
|
4506
|
+
const isLoading = !profileSettled;
|
|
4507
|
+
const expiredHandledRef = (0, import_react16.useRef)(false);
|
|
4508
|
+
(0, import_react16.useEffect)(() => {
|
|
4509
|
+
const unsubscribe = auth.onSessionExpired(() => {
|
|
4510
|
+
if (expiredHandledRef.current) return;
|
|
4511
|
+
expiredHandledRef.current = true;
|
|
4512
|
+
authLogger.warn("Session expired (terminal 401) \u2014 redirecting to login");
|
|
4513
|
+
clearProfileCache();
|
|
4514
|
+
Analytics.event("auth_session_expired" /* AUTH_SESSION_EXPIRED */, {
|
|
4515
|
+
category: "auth" /* AUTH */
|
|
4516
|
+
});
|
|
4517
|
+
router.hardReplace(routesRef.current.defaultAuthCallback);
|
|
4518
|
+
});
|
|
4519
|
+
return unsubscribe;
|
|
4520
|
+
}, [router]);
|
|
4521
|
+
(0, import_react16.useEffect)(() => {
|
|
4522
|
+
if (!isAuthenticated || pendingManualNavRef.current) return;
|
|
4523
|
+
if (pathname === routes.auth && !queryParams.get("flow")) {
|
|
4524
|
+
const saved = redirectManager.hasRedirect() ? redirectManager.getRedirect() : null;
|
|
4525
|
+
if (saved) redirectManager.clearRedirect();
|
|
4526
|
+
router.push(saved || routes.defaultCallback);
|
|
4958
4527
|
}
|
|
4959
|
-
}, [
|
|
4960
|
-
(0,
|
|
4961
|
-
if (
|
|
4962
|
-
|
|
4963
|
-
authLogger.info("Initializing auth...");
|
|
4964
|
-
const isInIframe = typeof window !== "undefined" && window.self !== window.top;
|
|
4965
|
-
authLogger.info("Is in iframe:", isInIframe);
|
|
4966
|
-
const token = CfgAccountsApi.getToken();
|
|
4967
|
-
const refreshToken2 = CfgAccountsApi.getRefreshToken();
|
|
4968
|
-
authLogger.info("Token from API:", token ? `${token.substring(0, 20)}...` : "null");
|
|
4969
|
-
authLogger.info("Refresh token from API:", refreshToken2 ? `${refreshToken2.substring(0, 20)}...` : "null");
|
|
4970
|
-
authLogger.info("localStorage keys:", Object.keys(localStorage).filter((k) => k.includes("token") || k.includes("auth")));
|
|
4971
|
-
if (!isInIframe && isSessionDeadOnBootstrap(token, refreshToken2)) {
|
|
4972
|
-
authLogger.warn("Bootstrap: dead session in storage (access + refresh both unusable) \u2014 clearing and showing login");
|
|
4973
|
-
clearAuthState("initializeAuth:deadSession");
|
|
4974
|
-
return;
|
|
4975
|
-
}
|
|
4976
|
-
const hasTokens = hasValidTokens();
|
|
4977
|
-
authLogger.info("Has tokens:", hasTokens);
|
|
4978
|
-
if (userRef.current) {
|
|
4979
|
-
authLogger.info("Profile already loaded from AccountsContext cache, skipping API request");
|
|
4980
|
-
setInitialized(true);
|
|
4981
|
-
setIsLoading(false);
|
|
4982
|
-
return;
|
|
4983
|
-
}
|
|
4984
|
-
const cachedProfile = getCachedProfile();
|
|
4985
|
-
if (cachedProfile) {
|
|
4986
|
-
authLogger.info("Profile found in localStorage cache, skipping API request");
|
|
4987
|
-
setInitialized(true);
|
|
4988
|
-
setIsLoading(false);
|
|
4989
|
-
return;
|
|
4990
|
-
}
|
|
4991
|
-
if (isInIframe && !hasTokens) {
|
|
4992
|
-
authLogger.info("Running in iframe without tokens - waiting for parent to send via postMessage");
|
|
4993
|
-
authLogger.info("AdminLayout will handle auth initialization, skipping AuthContext init");
|
|
4994
|
-
setInitialized(true);
|
|
4995
|
-
setIsLoading(false);
|
|
4996
|
-
return;
|
|
4997
|
-
}
|
|
4998
|
-
if (hasTokens) {
|
|
4999
|
-
setIsLoading(true);
|
|
5000
|
-
try {
|
|
5001
|
-
authLogger.info("No cached profile found, loading from API...");
|
|
5002
|
-
await loadCurrentProfile("AuthContext.initializeAuth");
|
|
5003
|
-
} catch (error) {
|
|
5004
|
-
authLogger.error("Failed to load profile during initialization:", error);
|
|
5005
|
-
const isAuthError = error?.status === 401 || error?.statusCode === 401 || error?.code === "token_not_valid" || error?.code === "authentication_failed";
|
|
5006
|
-
if (isAuthError) {
|
|
5007
|
-
clearAuthState("initializeAuth:authError");
|
|
5008
|
-
} else {
|
|
5009
|
-
authLogger.warn("Init profile load failed but keeping session");
|
|
5010
|
-
setInitialized(true);
|
|
5011
|
-
}
|
|
5012
|
-
}
|
|
5013
|
-
setIsLoading(false);
|
|
5014
|
-
} else {
|
|
5015
|
-
setInitialized(true);
|
|
5016
|
-
setIsLoading(false);
|
|
5017
|
-
}
|
|
5018
|
-
}, "initializeAuth");
|
|
5019
|
-
initializeAuth();
|
|
5020
|
-
}, [initialized]);
|
|
5021
|
-
(0, import_react17.useEffect)(() => {
|
|
5022
|
-
if (!initialized) return;
|
|
5023
|
-
const isAuthenticated = CfgAccountsApi.isAuthenticated();
|
|
5024
|
-
const authRoute = config?.routes?.auth || defaultRoutes.auth;
|
|
5025
|
-
const isAuthPage = pathname === authRoute;
|
|
5026
|
-
const flowParam = queryParams.get("flow");
|
|
5027
|
-
if (isAuthenticated && isAuthPage && !flowParam) {
|
|
5028
|
-
const callbackUrl = config?.routes?.defaultCallback || defaultRoutes.defaultCallback;
|
|
5029
|
-
router.push(callbackUrl);
|
|
4528
|
+
}, [isAuthenticated, pathname, queryParams, routes, router, redirectManager]);
|
|
4529
|
+
const loadCurrentProfile = (0, import_react16.useCallback)(async (callerId) => {
|
|
4530
|
+
if (!auth.isAuthenticated()) {
|
|
4531
|
+
throw new Error("No valid authentication token");
|
|
5030
4532
|
}
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
try {
|
|
5042
|
-
setIsLoading(true);
|
|
5043
|
-
const isAuthenticated = CfgAccountsApi.isAuthenticated();
|
|
5044
|
-
if (isAuthenticated) {
|
|
5045
|
-
await loadCurrentProfile();
|
|
5046
|
-
if (userRef.current) {
|
|
5047
|
-
pushToDefaultCallbackUrl();
|
|
5048
|
-
}
|
|
5049
|
-
} else {
|
|
5050
|
-
pushToDefaultAuthCallbackUrl();
|
|
5051
|
-
}
|
|
5052
|
-
} catch (error) {
|
|
5053
|
-
authLogger.error("Failed to check authentication:", error);
|
|
5054
|
-
if (!handleGlobalAuthError(error, "checkAuthAndRedirect")) {
|
|
5055
|
-
clearAuthState("checkAuthAndRedirect");
|
|
4533
|
+
await accountsRef.current.refreshProfile({
|
|
4534
|
+
callerId: callerId || "AuthContext.loadCurrentProfile"
|
|
4535
|
+
});
|
|
4536
|
+
}, []);
|
|
4537
|
+
const checkAuthAndRedirect = (0, import_react16.useCallback)(async () => {
|
|
4538
|
+
if (auth.isAuthenticated()) {
|
|
4539
|
+
try {
|
|
4540
|
+
await loadCurrentProfile("checkAuthAndRedirect");
|
|
4541
|
+
} catch (error) {
|
|
4542
|
+
authLogger.warn("checkAuthAndRedirect: profile load failed", error);
|
|
5056
4543
|
}
|
|
5057
|
-
|
|
5058
|
-
}
|
|
5059
|
-
|
|
4544
|
+
router.push(routesRef.current.defaultCallback);
|
|
4545
|
+
} else {
|
|
4546
|
+
router.push(routesRef.current.defaultAuthCallback);
|
|
5060
4547
|
}
|
|
5061
|
-
}, [loadCurrentProfile,
|
|
5062
|
-
const requestOTP = (0,
|
|
4548
|
+
}, [loadCurrentProfile, router]);
|
|
4549
|
+
const requestOTP = (0, import_react16.useCallback)(
|
|
5063
4550
|
async (identifier, sourceUrl) => {
|
|
5064
|
-
|
|
4551
|
+
auth.clearSession();
|
|
5065
4552
|
try {
|
|
5066
|
-
const result = await
|
|
4553
|
+
const result = await accountsRef.current.requestOTP({
|
|
5067
4554
|
identifier,
|
|
5068
4555
|
source_url: sourceUrl
|
|
5069
4556
|
});
|
|
@@ -5073,33 +4560,31 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5073
4560
|
});
|
|
5074
4561
|
return {
|
|
5075
4562
|
success: true,
|
|
5076
|
-
message: result.message ||
|
|
4563
|
+
message: result.message || "OTP code sent to your email address",
|
|
5077
4564
|
webmail: result.webmail ?? null
|
|
5078
4565
|
};
|
|
5079
4566
|
} catch (error) {
|
|
5080
4567
|
authLogger.error("Request OTP error:", error);
|
|
5081
4568
|
if (error instanceof APIError) {
|
|
5082
|
-
const
|
|
5083
|
-
const
|
|
4569
|
+
const body = error.response;
|
|
4570
|
+
const retryAfter = body?.retry_after ?? body?.retryAfter;
|
|
5084
4571
|
return {
|
|
5085
4572
|
success: false,
|
|
5086
4573
|
statusCode: error.statusCode,
|
|
5087
|
-
message,
|
|
4574
|
+
message: apiErrorMessage(error),
|
|
5088
4575
|
retryAfter: typeof retryAfter === "number" ? retryAfter : void 0
|
|
5089
4576
|
};
|
|
5090
4577
|
}
|
|
5091
|
-
return {
|
|
5092
|
-
success: false,
|
|
5093
|
-
message: "Failed to send OTP"
|
|
5094
|
-
};
|
|
4578
|
+
return { success: false, message: "Failed to send OTP" };
|
|
5095
4579
|
}
|
|
5096
4580
|
},
|
|
5097
|
-
[
|
|
4581
|
+
[]
|
|
5098
4582
|
);
|
|
5099
|
-
const verifyOTP = (0,
|
|
4583
|
+
const verifyOTP = (0, import_react16.useCallback)(
|
|
5100
4584
|
async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect) => {
|
|
5101
4585
|
try {
|
|
5102
|
-
|
|
4586
|
+
pendingManualNavRef.current = Boolean(skipRedirect);
|
|
4587
|
+
const result = await accountsRef.current.verifyOTP({
|
|
5103
4588
|
identifier,
|
|
5104
4589
|
otp: otpCode,
|
|
5105
4590
|
source_url: sourceUrl
|
|
@@ -5116,15 +4601,11 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5116
4601
|
}
|
|
5117
4602
|
if (!result.access || !result.refresh) {
|
|
5118
4603
|
authLogger.error("Verify OTP returned invalid response:", result);
|
|
5119
|
-
return {
|
|
5120
|
-
success: false,
|
|
5121
|
-
message: "Invalid OTP verification response"
|
|
5122
|
-
};
|
|
4604
|
+
return { success: false, message: "Invalid OTP verification response" };
|
|
5123
4605
|
}
|
|
5124
4606
|
if (identifier.includes("@")) {
|
|
5125
4607
|
setStoredEmail(identifier);
|
|
5126
4608
|
}
|
|
5127
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
5128
4609
|
Analytics.event("auth_login_success" /* AUTH_LOGIN_SUCCESS */, {
|
|
5129
4610
|
category: "auth" /* AUTH */,
|
|
5130
4611
|
label: "email"
|
|
@@ -5133,8 +4614,9 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5133
4614
|
Analytics.setUser(String(result.user.id));
|
|
5134
4615
|
}
|
|
5135
4616
|
if (!skipRedirect) {
|
|
5136
|
-
const
|
|
5137
|
-
|
|
4617
|
+
const saved = redirectManager.hasRedirect() ? redirectManager.getRedirect() : null;
|
|
4618
|
+
if (saved) redirectManager.clearRedirect();
|
|
4619
|
+
const finalRedirectUrl = saved || redirectUrl || routesRef.current.defaultCallback;
|
|
5138
4620
|
authLogger.info("Redirecting after auth to:", finalRedirectUrl);
|
|
5139
4621
|
router.hardPush(finalRedirectUrl);
|
|
5140
4622
|
}
|
|
@@ -5151,96 +4633,62 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5151
4633
|
label: "email"
|
|
5152
4634
|
});
|
|
5153
4635
|
if (error instanceof APIError) {
|
|
5154
|
-
|
|
5155
|
-
return { success: false, message };
|
|
4636
|
+
return { success: false, message: apiErrorMessage(error) };
|
|
5156
4637
|
}
|
|
5157
|
-
return {
|
|
5158
|
-
success: false,
|
|
5159
|
-
message: "Failed to verify OTP"
|
|
5160
|
-
};
|
|
4638
|
+
return { success: false, message: "Failed to verify OTP" };
|
|
5161
4639
|
}
|
|
5162
4640
|
},
|
|
5163
|
-
[setStoredEmail,
|
|
4641
|
+
[setStoredEmail, redirectManager, router]
|
|
5164
4642
|
);
|
|
5165
|
-
const refreshToken = (0,
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
if (!refreshTokenValue) {
|
|
5169
|
-
clearAuthState("refreshToken:noToken");
|
|
5170
|
-
Analytics.event("auth_session_expired" /* AUTH_SESSION_EXPIRED */, {
|
|
5171
|
-
category: "auth" /* AUTH */
|
|
5172
|
-
});
|
|
5173
|
-
return {
|
|
5174
|
-
success: false,
|
|
5175
|
-
message: "No refresh token available"
|
|
5176
|
-
};
|
|
5177
|
-
}
|
|
5178
|
-
await accounts.refreshToken(refreshTokenValue);
|
|
4643
|
+
const refreshToken = (0, import_react16.useCallback)(async () => {
|
|
4644
|
+
const access = await auth.refreshNow();
|
|
4645
|
+
if (access) {
|
|
5179
4646
|
Analytics.event("auth_token_refresh" /* AUTH_TOKEN_REFRESH */, {
|
|
5180
4647
|
category: "auth" /* AUTH */
|
|
5181
4648
|
});
|
|
5182
|
-
return {
|
|
5183
|
-
success: true,
|
|
5184
|
-
message: "Token refreshed"
|
|
5185
|
-
};
|
|
5186
|
-
} catch (error) {
|
|
5187
|
-
authLogger.error("Refresh token error:", error);
|
|
5188
|
-
clearAuthState("refreshToken:error");
|
|
5189
|
-
Analytics.event("auth_token_refresh_fail" /* AUTH_TOKEN_REFRESH_FAIL */, {
|
|
5190
|
-
category: "auth" /* AUTH */
|
|
5191
|
-
});
|
|
5192
|
-
return {
|
|
5193
|
-
success: false,
|
|
5194
|
-
message: "Error refreshing token"
|
|
5195
|
-
};
|
|
4649
|
+
return { success: true, message: "Token refreshed" };
|
|
5196
4650
|
}
|
|
5197
|
-
|
|
5198
|
-
|
|
4651
|
+
Analytics.event("auth_token_refresh_fail" /* AUTH_TOKEN_REFRESH_FAIL */, {
|
|
4652
|
+
category: "auth" /* AUTH */
|
|
4653
|
+
});
|
|
4654
|
+
return { success: false, message: "Error refreshing token" };
|
|
4655
|
+
}, []);
|
|
4656
|
+
const logout = (0, import_react16.useCallback)(() => {
|
|
5199
4657
|
Analytics.event("auth_logout" /* AUTH_LOGOUT */, {
|
|
5200
4658
|
category: "auth" /* AUTH */
|
|
5201
4659
|
});
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
const isAdminUser = (0, import_react17.useMemo)(() => {
|
|
5209
|
-
return Boolean(user?.is_staff || user?.is_superuser);
|
|
5210
|
-
}, [user]);
|
|
5211
|
-
(0, import_react17.useEffect)(() => {
|
|
4660
|
+
accountsRef.current.logout();
|
|
4661
|
+
router.hardReplace(routesRef.current.defaultAuthCallback);
|
|
4662
|
+
}, [router]);
|
|
4663
|
+
const user = accounts.profile ?? null;
|
|
4664
|
+
const isAdminUser = Boolean(user?.is_staff || user?.is_superuser);
|
|
4665
|
+
(0, import_react16.useEffect)(() => {
|
|
5212
4666
|
applyRoleLogPolicy({ isAdmin: isAdminUser });
|
|
5213
4667
|
}, [isAdminUser]);
|
|
5214
|
-
const updateProfile = (0,
|
|
4668
|
+
const updateProfile = (0, import_react16.useCallback)(
|
|
5215
4669
|
async (data) => {
|
|
5216
|
-
const result = await
|
|
5217
|
-
return result;
|
|
5218
|
-
},
|
|
5219
|
-
[accounts]
|
|
5220
|
-
);
|
|
5221
|
-
const uploadAvatar = (0, import_react17.useCallback)(
|
|
5222
|
-
async (avatar) => {
|
|
5223
|
-
const result = await accounts.uploadAvatar(avatar);
|
|
4670
|
+
const result = await accountsRef.current.partialUpdateProfile(data);
|
|
5224
4671
|
return result;
|
|
5225
4672
|
},
|
|
5226
|
-
[
|
|
4673
|
+
[]
|
|
5227
4674
|
);
|
|
5228
|
-
const
|
|
4675
|
+
const uploadAvatar = (0, import_react16.useCallback)(async (avatar) => {
|
|
4676
|
+
const result = await accountsRef.current.uploadAvatar(avatar);
|
|
4677
|
+
return result;
|
|
4678
|
+
}, []);
|
|
4679
|
+
const value = (0, import_react16.useMemo)(
|
|
5229
4680
|
() => ({
|
|
5230
4681
|
user,
|
|
5231
4682
|
isLoading,
|
|
5232
|
-
//
|
|
5233
|
-
//
|
|
5234
|
-
|
|
5235
|
-
// unauthenticated WITHOUT waiting for a server 401 (which CSP / a hung
|
|
5236
|
-
// request / offline can swallow). `authTick` (in deps) forces recompute
|
|
5237
|
-
// after tokens are cleared/set, since token state lives in storage.
|
|
5238
|
-
isAuthenticated: isSessionAlive(),
|
|
4683
|
+
// Reactive: flips via the store snapshot (token writes, refreshes,
|
|
4684
|
+
// expiry timer, cross-tab/cross-store sync) — no ticks, no polling.
|
|
4685
|
+
isAuthenticated,
|
|
5239
4686
|
isAdminUser,
|
|
4687
|
+
routes,
|
|
5240
4688
|
loadCurrentProfile,
|
|
5241
4689
|
checkAuthAndRedirect,
|
|
5242
|
-
getToken: /* @__PURE__ */ __name(() =>
|
|
5243
|
-
getRefreshToken: /* @__PURE__ */ __name(() =>
|
|
4690
|
+
getToken: /* @__PURE__ */ __name(() => auth.getToken(), "getToken"),
|
|
4691
|
+
getRefreshToken: /* @__PURE__ */ __name(() => auth.getRefreshToken(), "getRefreshToken"),
|
|
5244
4692
|
getSavedEmail: /* @__PURE__ */ __name(() => storedEmail, "getSavedEmail"),
|
|
5245
4693
|
saveEmail: setStoredEmail,
|
|
5246
4694
|
clearSavedEmail: clearStoredEmail,
|
|
@@ -5248,21 +4696,19 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5248
4696
|
verifyOTP,
|
|
5249
4697
|
refreshToken,
|
|
5250
4698
|
logout,
|
|
5251
|
-
// Redirect URL methods
|
|
5252
4699
|
saveRedirectUrl: redirectManager.setRedirect,
|
|
5253
4700
|
getRedirectUrl: redirectManager.getFinalRedirectUrl,
|
|
5254
4701
|
clearRedirectUrl: redirectManager.clearRedirect,
|
|
5255
4702
|
hasRedirectUrl: redirectManager.hasRedirect,
|
|
5256
|
-
// Profile management
|
|
5257
4703
|
updateProfile,
|
|
5258
4704
|
uploadAvatar
|
|
5259
4705
|
}),
|
|
5260
4706
|
[
|
|
5261
4707
|
user,
|
|
5262
4708
|
isLoading,
|
|
5263
|
-
|
|
5264
|
-
// recompute isAuthenticated when tokens are cleared/set
|
|
4709
|
+
isAuthenticated,
|
|
5265
4710
|
isAdminUser,
|
|
4711
|
+
routes,
|
|
5266
4712
|
loadCurrentProfile,
|
|
5267
4713
|
checkAuthAndRedirect,
|
|
5268
4714
|
storedEmail,
|
|
@@ -5277,19 +4723,20 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5277
4723
|
uploadAvatar
|
|
5278
4724
|
]
|
|
5279
4725
|
);
|
|
5280
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthContext.Provider, { value, children
|
|
4726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthContext.Provider, { value, children });
|
|
5281
4727
|
}, "AuthProviderInternal");
|
|
5282
4728
|
function AuthProviderRaw({ children, config, enabled = true }) {
|
|
5283
4729
|
if (!enabled) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
|
|
5284
4730
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AccountsProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AuthProviderInternal, { config, children }) });
|
|
5285
4731
|
}
|
|
5286
4732
|
__name(AuthProviderRaw, "AuthProviderRaw");
|
|
5287
|
-
var AuthProvider = (0,
|
|
4733
|
+
var AuthProvider = (0, import_react16.memo)(AuthProviderRaw);
|
|
5288
4734
|
var defaultAuthState = {
|
|
5289
4735
|
user: null,
|
|
5290
4736
|
isLoading: false,
|
|
5291
4737
|
isAuthenticated: false,
|
|
5292
4738
|
isAdminUser: false,
|
|
4739
|
+
routes: resolveAuthRoutes(),
|
|
5293
4740
|
loadCurrentProfile: /* @__PURE__ */ __name(async () => {
|
|
5294
4741
|
authLogger.warn("useAuth: loadCurrentProfile called outside AuthProvider");
|
|
5295
4742
|
}, "loadCurrentProfile"),
|
|
@@ -5316,12 +4763,12 @@ var defaultAuthState = {
|
|
|
5316
4763
|
authLogger.warn("useAuth: refreshToken called outside AuthProvider");
|
|
5317
4764
|
return { success: false, message: "AuthProvider not available" };
|
|
5318
4765
|
}, "refreshToken"),
|
|
5319
|
-
logout: /* @__PURE__ */ __name(
|
|
4766
|
+
logout: /* @__PURE__ */ __name(() => {
|
|
5320
4767
|
authLogger.warn("useAuth: logout called outside AuthProvider");
|
|
5321
4768
|
}, "logout"),
|
|
5322
4769
|
saveRedirectUrl: /* @__PURE__ */ __name(() => {
|
|
5323
4770
|
}, "saveRedirectUrl"),
|
|
5324
|
-
getRedirectUrl: /* @__PURE__ */ __name(() =>
|
|
4771
|
+
getRedirectUrl: /* @__PURE__ */ __name(() => "", "getRedirectUrl"),
|
|
5325
4772
|
clearRedirectUrl: /* @__PURE__ */ __name(() => {
|
|
5326
4773
|
}, "clearRedirectUrl"),
|
|
5327
4774
|
hasRedirectUrl: /* @__PURE__ */ __name(() => false, "hasRedirectUrl"),
|
|
@@ -5335,9 +4782,9 @@ var defaultAuthState = {
|
|
|
5335
4782
|
}, "uploadAvatar")
|
|
5336
4783
|
};
|
|
5337
4784
|
var useAuth = /* @__PURE__ */ __name(() => {
|
|
5338
|
-
const context = (0,
|
|
4785
|
+
const context = (0, import_react16.useContext)(AuthContext);
|
|
5339
4786
|
if (context === void 0) {
|
|
5340
|
-
if (
|
|
4787
|
+
if (isBrowser2 && isDev) {
|
|
5341
4788
|
authLogger.debug("useAuth called outside AuthProvider, returning default state");
|
|
5342
4789
|
}
|
|
5343
4790
|
return defaultAuthState;
|
|
@@ -5368,4 +4815,20 @@ var formatAuthError = /* @__PURE__ */ __name((error) => {
|
|
|
5368
4815
|
}
|
|
5369
4816
|
return "An unexpected error occurred";
|
|
5370
4817
|
}, "formatAuthError");
|
|
4818
|
+
|
|
4819
|
+
// src/auth/utils/guard.ts
|
|
4820
|
+
function resolveGuardIsLoading(s) {
|
|
4821
|
+
if (!s.mounted) return true;
|
|
4822
|
+
if (!s.requireAuth) return false;
|
|
4823
|
+
return s.authLoading || s.isRedirecting || !s.isAuthenticated;
|
|
4824
|
+
}
|
|
4825
|
+
__name(resolveGuardIsLoading, "resolveGuardIsLoading");
|
|
4826
|
+
function shouldRedirectToAuth(s) {
|
|
4827
|
+
return s.mounted && s.requireAuth && !s.authLoading && !s.isAuthenticated && !s.isRedirecting;
|
|
4828
|
+
}
|
|
4829
|
+
__name(shouldRedirectToAuth, "shouldRedirectToAuth");
|
|
4830
|
+
function resolveGuardIsAuthenticated(s) {
|
|
4831
|
+
return !s.requireAuth || s.isAuthenticated;
|
|
4832
|
+
}
|
|
4833
|
+
__name(resolveGuardIsAuthenticated, "resolveGuardIsAuthenticated");
|
|
5371
4834
|
//# sourceMappingURL=auth.cjs.map
|