@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/hooks.cjs
CHANGED
|
@@ -999,6 +999,89 @@ var _onUnauthorized = null;
|
|
|
999
999
|
var _refreshHandler = null;
|
|
1000
1000
|
var _refreshInflight = null;
|
|
1001
1001
|
var RETRY_MARKER = "X-Auth-Retry";
|
|
1002
|
+
function jwtExpMs(token) {
|
|
1003
|
+
try {
|
|
1004
|
+
const payload = token.split(".")[1];
|
|
1005
|
+
if (!payload) return null;
|
|
1006
|
+
const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
1007
|
+
return typeof json.exp === "number" ? json.exp * 1e3 : null;
|
|
1008
|
+
} catch {
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
__name(jwtExpMs, "jwtExpMs");
|
|
1013
|
+
function computeSnapshot() {
|
|
1014
|
+
const access = _storage.get(ACCESS_KEY);
|
|
1015
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
1016
|
+
const now = Date.now();
|
|
1017
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
1018
|
+
const accessAlive = access !== null && (accessExp === null || accessExp > now);
|
|
1019
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
1020
|
+
const refreshAlive = refresh !== null && (refreshExp === null || refreshExp > now);
|
|
1021
|
+
return {
|
|
1022
|
+
status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
|
|
1023
|
+
accessExpiresAt: accessExp
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
__name(computeSnapshot, "computeSnapshot");
|
|
1027
|
+
var SERVER_SNAPSHOT = { status: "anonymous", accessExpiresAt: null };
|
|
1028
|
+
var SESSION_SYNC_EVENT = `cfg-auth:changed:${ACCESS_KEY}`;
|
|
1029
|
+
var _snapshot = computeSnapshot();
|
|
1030
|
+
var _sessionListeners = /* @__PURE__ */ new Set();
|
|
1031
|
+
var _expiryTimer = null;
|
|
1032
|
+
var _storageListenerInstalled = false;
|
|
1033
|
+
function scheduleExpiryFlip() {
|
|
1034
|
+
if (!isBrowser) return;
|
|
1035
|
+
if (_expiryTimer !== null) {
|
|
1036
|
+
clearTimeout(_expiryTimer);
|
|
1037
|
+
_expiryTimer = null;
|
|
1038
|
+
}
|
|
1039
|
+
if (_sessionListeners.size === 0) return;
|
|
1040
|
+
const now = Date.now();
|
|
1041
|
+
const exps = [];
|
|
1042
|
+
const access = _storage.get(ACCESS_KEY);
|
|
1043
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
1044
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
1045
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
1046
|
+
if (accessExp !== null && accessExp > now) exps.push(accessExp);
|
|
1047
|
+
if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
|
|
1048
|
+
if (!exps.length) return;
|
|
1049
|
+
const delay = Math.min(Math.min(...exps) - now + 250, 2147483647);
|
|
1050
|
+
_expiryTimer = setTimeout(notifySessionChanged, delay);
|
|
1051
|
+
}
|
|
1052
|
+
__name(scheduleExpiryFlip, "scheduleExpiryFlip");
|
|
1053
|
+
function notifySessionChanged() {
|
|
1054
|
+
const next = computeSnapshot();
|
|
1055
|
+
const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
|
|
1056
|
+
if (changed) _snapshot = next;
|
|
1057
|
+
scheduleExpiryFlip();
|
|
1058
|
+
if (!changed) return;
|
|
1059
|
+
for (const listener of Array.from(_sessionListeners)) {
|
|
1060
|
+
try {
|
|
1061
|
+
listener();
|
|
1062
|
+
} catch {
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
if (isBrowser) {
|
|
1066
|
+
try {
|
|
1067
|
+
window.dispatchEvent(new Event(SESSION_SYNC_EVENT));
|
|
1068
|
+
} catch {
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
__name(notifySessionChanged, "notifySessionChanged");
|
|
1073
|
+
function ensureStorageSync() {
|
|
1074
|
+
if (!isBrowser || _storageListenerInstalled) return;
|
|
1075
|
+
_storageListenerInstalled = true;
|
|
1076
|
+
window.addEventListener("storage", (e) => {
|
|
1077
|
+
if (e.key === null || e.key === ACCESS_KEY || e.key === REFRESH_KEY) {
|
|
1078
|
+
notifySessionChanged();
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
|
|
1082
|
+
}
|
|
1083
|
+
__name(ensureStorageSync, "ensureStorageSync");
|
|
1084
|
+
var _sessionExpiredHandlers = /* @__PURE__ */ new Set();
|
|
1002
1085
|
var _client = null;
|
|
1003
1086
|
function pushClientConfig() {
|
|
1004
1087
|
if (!_client) return;
|
|
@@ -1016,6 +1099,7 @@ var auth = {
|
|
|
1016
1099
|
setStorageMode(mode) {
|
|
1017
1100
|
_storageMode = mode;
|
|
1018
1101
|
_storage = mode === "cookie" ? cookieBackend : localStorageBackend;
|
|
1102
|
+
notifySessionChanged();
|
|
1019
1103
|
},
|
|
1020
1104
|
// ── Bearer token ──────────────────────────────────────────────────
|
|
1021
1105
|
getToken() {
|
|
@@ -1023,19 +1107,79 @@ var auth = {
|
|
|
1023
1107
|
},
|
|
1024
1108
|
setToken(token) {
|
|
1025
1109
|
_storage.set(ACCESS_KEY, token);
|
|
1110
|
+
notifySessionChanged();
|
|
1026
1111
|
},
|
|
1027
1112
|
getRefreshToken() {
|
|
1028
1113
|
return _storage.get(REFRESH_KEY);
|
|
1029
1114
|
},
|
|
1030
1115
|
setRefreshToken(token) {
|
|
1031
1116
|
_storage.set(REFRESH_KEY, token);
|
|
1117
|
+
notifySessionChanged();
|
|
1032
1118
|
},
|
|
1033
1119
|
clearTokens() {
|
|
1034
1120
|
_storage.set(ACCESS_KEY, null);
|
|
1035
1121
|
_storage.set(REFRESH_KEY, null);
|
|
1122
|
+
notifySessionChanged();
|
|
1036
1123
|
},
|
|
1124
|
+
/** Session-aware: token PRESENT and not past its `exp` (or a live refresh
|
|
1125
|
+
* token exists that can mint one). Prefer `getSnapshot().status` in React. */
|
|
1037
1126
|
isAuthenticated() {
|
|
1038
|
-
return
|
|
1127
|
+
return computeSnapshot().status === "authenticated";
|
|
1128
|
+
},
|
|
1129
|
+
// ── Session (the ONE write path for login/logout flows) ──────────
|
|
1130
|
+
/**
|
|
1131
|
+
* Persist a token pair atomically. Every login flow (OTP verify, 2FA,
|
|
1132
|
+
* OAuth callback) and the refresh handler should end here — do NOT
|
|
1133
|
+
* scatter `setToken`/`setRefreshToken` pairs through app code.
|
|
1134
|
+
* `refresh: undefined` keeps the current refresh token (access-only
|
|
1135
|
+
* rotation); `refresh: null` explicitly drops it.
|
|
1136
|
+
*/
|
|
1137
|
+
setSession(tokens) {
|
|
1138
|
+
_storage.set(ACCESS_KEY, tokens.access);
|
|
1139
|
+
if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
|
|
1140
|
+
notifySessionChanged();
|
|
1141
|
+
},
|
|
1142
|
+
clearSession() {
|
|
1143
|
+
auth.clearTokens();
|
|
1144
|
+
},
|
|
1145
|
+
// ── Reactive snapshot (for useSyncExternalStore) ──────────────────
|
|
1146
|
+
/**
|
|
1147
|
+
* @example React:
|
|
1148
|
+
* const session = useSyncExternalStore(
|
|
1149
|
+
* auth.subscribe, auth.getSnapshot, auth.getServerSnapshot,
|
|
1150
|
+
* );
|
|
1151
|
+
* const isAuthenticated = session.status === 'authenticated';
|
|
1152
|
+
*/
|
|
1153
|
+
getSnapshot() {
|
|
1154
|
+
return _snapshot;
|
|
1155
|
+
},
|
|
1156
|
+
getServerSnapshot() {
|
|
1157
|
+
return SERVER_SNAPSHOT;
|
|
1158
|
+
},
|
|
1159
|
+
subscribe(listener) {
|
|
1160
|
+
ensureStorageSync();
|
|
1161
|
+
_sessionListeners.add(listener);
|
|
1162
|
+
notifySessionChanged();
|
|
1163
|
+
return () => {
|
|
1164
|
+
_sessionListeners.delete(listener);
|
|
1165
|
+
if (_sessionListeners.size === 0 && _expiryTimer !== null) {
|
|
1166
|
+
clearTimeout(_expiryTimer);
|
|
1167
|
+
_expiryTimer = null;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
},
|
|
1171
|
+
/**
|
|
1172
|
+
* Fired on TERMINAL 401 — after the refresh+retry path is exhausted.
|
|
1173
|
+
* The store has already cleared the session before calling back, so
|
|
1174
|
+
* handlers only need to route to login (no clear-then-redirect
|
|
1175
|
+
* ordering to get wrong). Returns an unsubscribe function; multiple
|
|
1176
|
+
* handlers compose (unlike the legacy single-slot `onUnauthorized`).
|
|
1177
|
+
*/
|
|
1178
|
+
onSessionExpired(cb) {
|
|
1179
|
+
_sessionExpiredHandlers.add(cb);
|
|
1180
|
+
return () => {
|
|
1181
|
+
_sessionExpiredHandlers.delete(cb);
|
|
1182
|
+
};
|
|
1039
1183
|
},
|
|
1040
1184
|
// ── API key ───────────────────────────────────────────────────────
|
|
1041
1185
|
getApiKey() {
|
|
@@ -1117,15 +1261,22 @@ var auth = {
|
|
|
1117
1261
|
async function tryRefresh() {
|
|
1118
1262
|
if (_refreshInflight) return _refreshInflight;
|
|
1119
1263
|
if (!_refreshHandler) return null;
|
|
1120
|
-
const
|
|
1121
|
-
|
|
1264
|
+
const runRefresh = /* @__PURE__ */ __name(async () => {
|
|
1265
|
+
const refresh = auth.getRefreshToken();
|
|
1266
|
+
if (!refresh) return null;
|
|
1267
|
+
const result = await _refreshHandler(refresh);
|
|
1268
|
+
if (!result?.access) return null;
|
|
1269
|
+
auth.setToken(result.access);
|
|
1270
|
+
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
1271
|
+
return result.access;
|
|
1272
|
+
}, "runRefresh");
|
|
1122
1273
|
_refreshInflight = (async () => {
|
|
1123
1274
|
try {
|
|
1124
|
-
const
|
|
1125
|
-
if (
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
return
|
|
1275
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
1276
|
+
if (locks?.request) {
|
|
1277
|
+
return await locks.request(`${REFRESH_KEY}:refresh`, runRefresh);
|
|
1278
|
+
}
|
|
1279
|
+
return await runRefresh();
|
|
1129
1280
|
} catch {
|
|
1130
1281
|
return null;
|
|
1131
1282
|
} finally {
|
|
@@ -1135,6 +1286,22 @@ async function tryRefresh() {
|
|
|
1135
1286
|
return _refreshInflight;
|
|
1136
1287
|
}
|
|
1137
1288
|
__name(tryRefresh, "tryRefresh");
|
|
1289
|
+
function expireSession(response) {
|
|
1290
|
+
auth.clearTokens();
|
|
1291
|
+
for (const cb of Array.from(_sessionExpiredHandlers)) {
|
|
1292
|
+
try {
|
|
1293
|
+
cb(response);
|
|
1294
|
+
} catch {
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (_onUnauthorized) {
|
|
1298
|
+
try {
|
|
1299
|
+
_onUnauthorized(response);
|
|
1300
|
+
} catch {
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
__name(expireSession, "expireSession");
|
|
1138
1305
|
function dpopEnabled() {
|
|
1139
1306
|
try {
|
|
1140
1307
|
return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
@@ -1264,22 +1431,12 @@ function installAuthOnClient(client2) {
|
|
|
1264
1431
|
client2.interceptors.response.use(async (response, request) => {
|
|
1265
1432
|
if (response.status !== 401) return response;
|
|
1266
1433
|
if (request.headers.get(RETRY_MARKER)) {
|
|
1267
|
-
|
|
1268
|
-
try {
|
|
1269
|
-
_onUnauthorized(response);
|
|
1270
|
-
} catch {
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1434
|
+
expireSession(response);
|
|
1273
1435
|
return response;
|
|
1274
1436
|
}
|
|
1275
1437
|
const newToken = await tryRefresh();
|
|
1276
1438
|
if (!newToken) {
|
|
1277
|
-
|
|
1278
|
-
try {
|
|
1279
|
-
_onUnauthorized(response);
|
|
1280
|
-
} catch {
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1439
|
+
expireSession(response);
|
|
1283
1440
|
return response;
|
|
1284
1441
|
}
|
|
1285
1442
|
const retry = request.clone();
|
|
@@ -1291,25 +1448,36 @@ function installAuthOnClient(client2) {
|
|
|
1291
1448
|
}
|
|
1292
1449
|
try {
|
|
1293
1450
|
const retried = await fetch(retry);
|
|
1294
|
-
if (retried.status === 401
|
|
1295
|
-
try {
|
|
1296
|
-
_onUnauthorized(retried);
|
|
1297
|
-
} catch {
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1451
|
+
if (retried.status === 401) expireSession(retried);
|
|
1300
1452
|
return retried;
|
|
1301
1453
|
} catch {
|
|
1302
|
-
if (_onUnauthorized) {
|
|
1303
|
-
try {
|
|
1304
|
-
_onUnauthorized(response);
|
|
1305
|
-
} catch {
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
1454
|
return response;
|
|
1309
1455
|
}
|
|
1310
1456
|
});
|
|
1311
1457
|
}
|
|
1312
1458
|
__name(installAuthOnClient, "installAuthOnClient");
|
|
1459
|
+
var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
|
|
1460
|
+
auth.setRefreshHandler(async (refresh) => {
|
|
1461
|
+
try {
|
|
1462
|
+
const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
|
|
1463
|
+
const headers = { "Content-Type": "application/json" };
|
|
1464
|
+
if (dpopEnabled() && typeof window !== "undefined") {
|
|
1465
|
+
const proof = await _makeDpopProof("POST", url);
|
|
1466
|
+
if (proof) headers["DPoP"] = proof;
|
|
1467
|
+
}
|
|
1468
|
+
const res = await fetch(url, {
|
|
1469
|
+
method: "POST",
|
|
1470
|
+
headers,
|
|
1471
|
+
credentials: auth.getWithCredentials() ? "include" : "same-origin",
|
|
1472
|
+
body: JSON.stringify({ refresh })
|
|
1473
|
+
});
|
|
1474
|
+
if (!res.ok) return null;
|
|
1475
|
+
const data = await res.json();
|
|
1476
|
+
return data?.access ? { access: data.access, refresh: data.refresh ?? refresh } : null;
|
|
1477
|
+
} catch {
|
|
1478
|
+
return null;
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1313
1481
|
|
|
1314
1482
|
// src/_api/generated/client.gen.ts
|
|
1315
1483
|
var client = createClient(createConfig({ baseUrl: "http://localhost:8000" }));
|