@djangocfg/api 2.1.450 → 2.1.452
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 +70 -8
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +105 -2
- package/dist/auth.d.ts +105 -2
- package/dist/auth.mjs +70 -8
- package/dist/auth.mjs.map +1 -1
- package/package.json +4 -2
- package/src/auth/__tests__/authRedirect.test.ts +101 -0
- package/src/auth/__tests__/base64.test.ts +53 -0
- package/src/auth/__tests__/constants.test.ts +30 -0
- package/src/auth/__tests__/formatAuthError.test.ts +48 -0
- package/src/auth/__tests__/guard.test.ts +144 -0
- package/src/auth/__tests__/path.test.ts +67 -0
- package/src/auth/__tests__/profileCache.test.ts +138 -0
- package/src/auth/__tests__/sessionBootstrap.test.ts +35 -0
- package/src/auth/__tests__/useAuthForm.2fa.test.ts +28 -18
- package/src/auth/__tests__/useTokenRefresh.dom.test.tsx +167 -0
- package/src/auth/__tests__/useTwoFactorStatus.test.ts +45 -36
- package/src/auth/__tests__/validation.test.ts +54 -0
- package/src/auth/context/AuthContext.tsx +55 -5
- package/src/auth/hooks/useAutoAuth.ts +5 -17
- package/src/auth/utils/guard.ts +79 -0
- package/src/auth/utils/index.ts +9 -0
- package/src/auth/utils/path.ts +34 -0
package/dist/auth.cjs
CHANGED
|
@@ -46,9 +46,18 @@ __export(auth_exports, {
|
|
|
46
46
|
formatAuthError: () => formatAuthError,
|
|
47
47
|
getCacheMetadata: () => getCacheMetadata,
|
|
48
48
|
getCachedProfile: () => getCachedProfile,
|
|
49
|
+
getTokenExpiry: () => getTokenExpiry,
|
|
49
50
|
hasValidCache: () => hasValidCache,
|
|
51
|
+
isAllowedAuthPath: () => isAllowedAuthPath,
|
|
52
|
+
isTokenExpired: () => isTokenExpired,
|
|
53
|
+
isTokenExpiringSoon: () => isTokenExpiringSoon,
|
|
50
54
|
logger: () => logger,
|
|
55
|
+
nextAuthEvaluationDelay: () => nextAuthEvaluationDelay,
|
|
56
|
+
normalizePath: () => normalizePath,
|
|
57
|
+
resolveGuardIsAuthenticated: () => resolveGuardIsAuthenticated,
|
|
58
|
+
resolveGuardIsLoading: () => resolveGuardIsLoading,
|
|
51
59
|
setCachedProfile: () => setCachedProfile,
|
|
60
|
+
shouldRedirectToAuth: () => shouldRedirectToAuth,
|
|
52
61
|
useAccountsContext: () => useAccountsContext,
|
|
53
62
|
useAuth: () => useAuth,
|
|
54
63
|
useAuthForm: () => useAuthForm,
|
|
@@ -348,21 +357,28 @@ var authLogger = logger.withTag("auth");
|
|
|
348
357
|
// src/auth/hooks/useAutoAuth.ts
|
|
349
358
|
var import_navigation3 = require("next/navigation");
|
|
350
359
|
var import_react5 = require("react");
|
|
360
|
+
|
|
361
|
+
// src/auth/utils/path.ts
|
|
351
362
|
var normalizePath = /* @__PURE__ */ __name((path) => {
|
|
352
363
|
if (!path) return "";
|
|
353
364
|
const withoutLocale = path.replace(/^\/[a-z]{2}(?=\/|$)/, "");
|
|
354
365
|
const trimmed = withoutLocale.replace(/\/+$/, "");
|
|
355
366
|
return trimmed || "/";
|
|
356
367
|
}, "normalizePath");
|
|
368
|
+
var isAllowedAuthPath = /* @__PURE__ */ __name((pathname, allowedPaths) => {
|
|
369
|
+
const normalized = normalizePath(pathname);
|
|
370
|
+
return allowedPaths.some(
|
|
371
|
+
(p) => normalized === p || normalized.startsWith(p + "/")
|
|
372
|
+
);
|
|
373
|
+
}, "isAllowedAuthPath");
|
|
374
|
+
|
|
375
|
+
// src/auth/hooks/useAutoAuth.ts
|
|
357
376
|
var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
|
|
358
377
|
const { onOTPDetected, cleanupUrl = true, allowedPaths = ["/auth"] } = options;
|
|
359
378
|
const queryParams = useQueryParams();
|
|
360
379
|
const pathname = (0, import_navigation3.usePathname)();
|
|
361
380
|
const router = useCfgRouter();
|
|
362
|
-
const
|
|
363
|
-
const isAllowedPath = allowedPaths.some(
|
|
364
|
-
(path) => normalizedPath === path || normalizedPath.startsWith(path + "/")
|
|
365
|
-
);
|
|
381
|
+
const isAllowedPath = isAllowedAuthPath(pathname, allowedPaths);
|
|
366
382
|
const queryOtp = queryParams.get("otp") || "";
|
|
367
383
|
const queryEmail = queryParams.get("email") || void 0;
|
|
368
384
|
const hasOTP = !!queryOtp;
|
|
@@ -4002,6 +4018,30 @@ var useDeleteAccount = /* @__PURE__ */ __name(() => {
|
|
|
4002
4018
|
};
|
|
4003
4019
|
}, "useDeleteAccount");
|
|
4004
4020
|
|
|
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
|
+
|
|
4005
4045
|
// src/auth/context/AccountsContext.tsx
|
|
4006
4046
|
var import_react16 = require("react");
|
|
4007
4047
|
|
|
@@ -4764,6 +4804,14 @@ var isSessionDeadOnBootstrap = /* @__PURE__ */ __name((accessToken, refreshToken
|
|
|
4764
4804
|
const refreshDead = isTokenExpired(refreshToken, 0, now);
|
|
4765
4805
|
return refreshDead;
|
|
4766
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");
|
|
4767
4815
|
var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
4768
4816
|
const accounts = useAccountsContext();
|
|
4769
4817
|
ensureRefreshHandler();
|
|
@@ -4781,6 +4829,17 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
4781
4829
|
const [initialized, setInitialized] = (0, import_react17.useState)(false);
|
|
4782
4830
|
const [authTick, setAuthTick] = (0, import_react17.useState)(0);
|
|
4783
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]);
|
|
4784
4843
|
const onUnauthorizedRef = (0, import_react17.useRef)(false);
|
|
4785
4844
|
const router = useCfgRouter();
|
|
4786
4845
|
const pathname = (0, import_navigation4.usePathname)();
|
|
@@ -5170,10 +5229,13 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
|
|
|
5170
5229
|
() => ({
|
|
5171
5230
|
user,
|
|
5172
5231
|
isLoading,
|
|
5173
|
-
//
|
|
5174
|
-
//
|
|
5175
|
-
//
|
|
5176
|
-
|
|
5232
|
+
// Authenticated iff the session is locally alive (access valid OR a usable
|
|
5233
|
+
// refresh token) — validated by `exp`, NOT by mere token presence. This is
|
|
5234
|
+
// what makes the guard self-sufficient: an expired session reads as
|
|
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(),
|
|
5177
5239
|
isAdminUser,
|
|
5178
5240
|
loadCurrentProfile,
|
|
5179
5241
|
checkAuthAndRedirect,
|