@djangocfg/api 2.1.449 → 2.1.450
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 +80 -20
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.mjs +80 -20
- package/dist/auth.mjs.map +1 -1
- package/package.json +2 -2
- package/src/auth/__tests__/jwt.test.ts +119 -0
- package/src/auth/__tests__/onUnauthorized.test.ts +206 -0
- package/src/auth/__tests__/sessionBootstrap.test.ts +76 -0
- package/src/auth/context/AuthContext.tsx +121 -13
- package/src/auth/hooks/useTokenRefresh.ts +3 -21
- package/src/auth/utils/jwt.ts +66 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT inspection helpers (browser-safe, dependency-free).
|
|
3
|
+
*
|
|
4
|
+
* These decode ONLY the `exp` claim to reason about token freshness on the
|
|
5
|
+
* client. They do NOT verify the signature — that is the server's job. The
|
|
6
|
+
* point is purely to avoid firing doomed requests with a token we can already
|
|
7
|
+
* see is malformed or expired, and to let auth bootstrap collapse a dead
|
|
8
|
+
* session to the login form instead of hanging on a preloader.
|
|
9
|
+
*
|
|
10
|
+
* A JWT that cannot be decoded is treated as EXPIRED/invalid (fail-closed):
|
|
11
|
+
* better to re-authenticate than to loop on 401s with garbage in storage.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Base64url-decode a JWT segment. Returns null on any malformation. */
|
|
15
|
+
function decodeSegment(segment: string): unknown {
|
|
16
|
+
try {
|
|
17
|
+
const base64 = segment.replace(/-/g, '+').replace(/_/g, '/');
|
|
18
|
+
// atob is defined in browsers and modern Node (global). Guard anyway.
|
|
19
|
+
if (typeof atob !== 'function') return null;
|
|
20
|
+
const json = atob(base64);
|
|
21
|
+
return JSON.parse(json);
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract the `exp` claim (epoch ms), or null if the token is undecodable or
|
|
29
|
+
* has no numeric `exp`.
|
|
30
|
+
*/
|
|
31
|
+
export function getTokenExpiry(token: string | null | undefined): number | null {
|
|
32
|
+
if (!token || typeof token !== 'string') return null;
|
|
33
|
+
const parts = token.split('.');
|
|
34
|
+
if (parts.length !== 3) return null; // not a well-formed JWT
|
|
35
|
+
const payload = decodeSegment(parts[1]);
|
|
36
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
37
|
+
const exp = (payload as { exp?: unknown }).exp;
|
|
38
|
+
if (typeof exp !== 'number' || !Number.isFinite(exp)) return null;
|
|
39
|
+
return exp * 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* True when the token is missing, malformed, or already past its `exp`.
|
|
44
|
+
* `skewMs` treats a token expiring within the skew window as already expired
|
|
45
|
+
* (default 0). Fail-closed: an undecodable token is considered expired.
|
|
46
|
+
*/
|
|
47
|
+
export function isTokenExpired(token: string | null | undefined, skewMs = 0, now = Date.now()): boolean {
|
|
48
|
+
const expiry = getTokenExpiry(token);
|
|
49
|
+
if (expiry === null) return true; // undecodable / no exp → treat as dead
|
|
50
|
+
return expiry - skewMs <= now;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* True when a valid token exists but expires within `thresholdMs` (used by the
|
|
55
|
+
* proactive refresher). A missing/undecodable token returns false here — that
|
|
56
|
+
* case is handled by the "expired" path, not the "expiring soon" path.
|
|
57
|
+
*/
|
|
58
|
+
export function isTokenExpiringSoon(
|
|
59
|
+
token: string | null | undefined,
|
|
60
|
+
thresholdMs: number,
|
|
61
|
+
now = Date.now(),
|
|
62
|
+
): boolean {
|
|
63
|
+
const expiry = getTokenExpiry(token);
|
|
64
|
+
if (expiry === null) return false;
|
|
65
|
+
return expiry - now < thresholdMs;
|
|
66
|
+
}
|