@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.mjs
CHANGED
|
@@ -966,6 +966,89 @@ var _onUnauthorized = null;
|
|
|
966
966
|
var _refreshHandler = null;
|
|
967
967
|
var _refreshInflight = null;
|
|
968
968
|
var RETRY_MARKER = "X-Auth-Retry";
|
|
969
|
+
function jwtExpMs(token) {
|
|
970
|
+
try {
|
|
971
|
+
const payload = token.split(".")[1];
|
|
972
|
+
if (!payload) return null;
|
|
973
|
+
const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
974
|
+
return typeof json.exp === "number" ? json.exp * 1e3 : null;
|
|
975
|
+
} catch {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
__name(jwtExpMs, "jwtExpMs");
|
|
980
|
+
function computeSnapshot() {
|
|
981
|
+
const access = _storage.get(ACCESS_KEY);
|
|
982
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
983
|
+
const now = Date.now();
|
|
984
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
985
|
+
const accessAlive = access !== null && (accessExp === null || accessExp > now);
|
|
986
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
987
|
+
const refreshAlive = refresh !== null && (refreshExp === null || refreshExp > now);
|
|
988
|
+
return {
|
|
989
|
+
status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
|
|
990
|
+
accessExpiresAt: accessExp
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
__name(computeSnapshot, "computeSnapshot");
|
|
994
|
+
var SERVER_SNAPSHOT = { status: "anonymous", accessExpiresAt: null };
|
|
995
|
+
var SESSION_SYNC_EVENT = `cfg-auth:changed:${ACCESS_KEY}`;
|
|
996
|
+
var _snapshot = computeSnapshot();
|
|
997
|
+
var _sessionListeners = /* @__PURE__ */ new Set();
|
|
998
|
+
var _expiryTimer = null;
|
|
999
|
+
var _storageListenerInstalled = false;
|
|
1000
|
+
function scheduleExpiryFlip() {
|
|
1001
|
+
if (!isBrowser) return;
|
|
1002
|
+
if (_expiryTimer !== null) {
|
|
1003
|
+
clearTimeout(_expiryTimer);
|
|
1004
|
+
_expiryTimer = null;
|
|
1005
|
+
}
|
|
1006
|
+
if (_sessionListeners.size === 0) return;
|
|
1007
|
+
const now = Date.now();
|
|
1008
|
+
const exps = [];
|
|
1009
|
+
const access = _storage.get(ACCESS_KEY);
|
|
1010
|
+
const refresh = _storage.get(REFRESH_KEY);
|
|
1011
|
+
const accessExp = access ? jwtExpMs(access) : null;
|
|
1012
|
+
const refreshExp = refresh ? jwtExpMs(refresh) : null;
|
|
1013
|
+
if (accessExp !== null && accessExp > now) exps.push(accessExp);
|
|
1014
|
+
if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
|
|
1015
|
+
if (!exps.length) return;
|
|
1016
|
+
const delay = Math.min(Math.min(...exps) - now + 250, 2147483647);
|
|
1017
|
+
_expiryTimer = setTimeout(notifySessionChanged, delay);
|
|
1018
|
+
}
|
|
1019
|
+
__name(scheduleExpiryFlip, "scheduleExpiryFlip");
|
|
1020
|
+
function notifySessionChanged() {
|
|
1021
|
+
const next = computeSnapshot();
|
|
1022
|
+
const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
|
|
1023
|
+
if (changed) _snapshot = next;
|
|
1024
|
+
scheduleExpiryFlip();
|
|
1025
|
+
if (!changed) return;
|
|
1026
|
+
for (const listener of Array.from(_sessionListeners)) {
|
|
1027
|
+
try {
|
|
1028
|
+
listener();
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (isBrowser) {
|
|
1033
|
+
try {
|
|
1034
|
+
window.dispatchEvent(new Event(SESSION_SYNC_EVENT));
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
__name(notifySessionChanged, "notifySessionChanged");
|
|
1040
|
+
function ensureStorageSync() {
|
|
1041
|
+
if (!isBrowser || _storageListenerInstalled) return;
|
|
1042
|
+
_storageListenerInstalled = true;
|
|
1043
|
+
window.addEventListener("storage", (e) => {
|
|
1044
|
+
if (e.key === null || e.key === ACCESS_KEY || e.key === REFRESH_KEY) {
|
|
1045
|
+
notifySessionChanged();
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
|
|
1049
|
+
}
|
|
1050
|
+
__name(ensureStorageSync, "ensureStorageSync");
|
|
1051
|
+
var _sessionExpiredHandlers = /* @__PURE__ */ new Set();
|
|
969
1052
|
var _client = null;
|
|
970
1053
|
function pushClientConfig() {
|
|
971
1054
|
if (!_client) return;
|
|
@@ -983,6 +1066,7 @@ var auth = {
|
|
|
983
1066
|
setStorageMode(mode) {
|
|
984
1067
|
_storageMode = mode;
|
|
985
1068
|
_storage = mode === "cookie" ? cookieBackend : localStorageBackend;
|
|
1069
|
+
notifySessionChanged();
|
|
986
1070
|
},
|
|
987
1071
|
// ── Bearer token ──────────────────────────────────────────────────
|
|
988
1072
|
getToken() {
|
|
@@ -990,19 +1074,79 @@ var auth = {
|
|
|
990
1074
|
},
|
|
991
1075
|
setToken(token) {
|
|
992
1076
|
_storage.set(ACCESS_KEY, token);
|
|
1077
|
+
notifySessionChanged();
|
|
993
1078
|
},
|
|
994
1079
|
getRefreshToken() {
|
|
995
1080
|
return _storage.get(REFRESH_KEY);
|
|
996
1081
|
},
|
|
997
1082
|
setRefreshToken(token) {
|
|
998
1083
|
_storage.set(REFRESH_KEY, token);
|
|
1084
|
+
notifySessionChanged();
|
|
999
1085
|
},
|
|
1000
1086
|
clearTokens() {
|
|
1001
1087
|
_storage.set(ACCESS_KEY, null);
|
|
1002
1088
|
_storage.set(REFRESH_KEY, null);
|
|
1089
|
+
notifySessionChanged();
|
|
1003
1090
|
},
|
|
1091
|
+
/** Session-aware: token PRESENT and not past its `exp` (or a live refresh
|
|
1092
|
+
* token exists that can mint one). Prefer `getSnapshot().status` in React. */
|
|
1004
1093
|
isAuthenticated() {
|
|
1005
|
-
return
|
|
1094
|
+
return computeSnapshot().status === "authenticated";
|
|
1095
|
+
},
|
|
1096
|
+
// ── Session (the ONE write path for login/logout flows) ──────────
|
|
1097
|
+
/**
|
|
1098
|
+
* Persist a token pair atomically. Every login flow (OTP verify, 2FA,
|
|
1099
|
+
* OAuth callback) and the refresh handler should end here — do NOT
|
|
1100
|
+
* scatter `setToken`/`setRefreshToken` pairs through app code.
|
|
1101
|
+
* `refresh: undefined` keeps the current refresh token (access-only
|
|
1102
|
+
* rotation); `refresh: null` explicitly drops it.
|
|
1103
|
+
*/
|
|
1104
|
+
setSession(tokens) {
|
|
1105
|
+
_storage.set(ACCESS_KEY, tokens.access);
|
|
1106
|
+
if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
|
|
1107
|
+
notifySessionChanged();
|
|
1108
|
+
},
|
|
1109
|
+
clearSession() {
|
|
1110
|
+
auth.clearTokens();
|
|
1111
|
+
},
|
|
1112
|
+
// ── Reactive snapshot (for useSyncExternalStore) ──────────────────
|
|
1113
|
+
/**
|
|
1114
|
+
* @example React:
|
|
1115
|
+
* const session = useSyncExternalStore(
|
|
1116
|
+
* auth.subscribe, auth.getSnapshot, auth.getServerSnapshot,
|
|
1117
|
+
* );
|
|
1118
|
+
* const isAuthenticated = session.status === 'authenticated';
|
|
1119
|
+
*/
|
|
1120
|
+
getSnapshot() {
|
|
1121
|
+
return _snapshot;
|
|
1122
|
+
},
|
|
1123
|
+
getServerSnapshot() {
|
|
1124
|
+
return SERVER_SNAPSHOT;
|
|
1125
|
+
},
|
|
1126
|
+
subscribe(listener) {
|
|
1127
|
+
ensureStorageSync();
|
|
1128
|
+
_sessionListeners.add(listener);
|
|
1129
|
+
notifySessionChanged();
|
|
1130
|
+
return () => {
|
|
1131
|
+
_sessionListeners.delete(listener);
|
|
1132
|
+
if (_sessionListeners.size === 0 && _expiryTimer !== null) {
|
|
1133
|
+
clearTimeout(_expiryTimer);
|
|
1134
|
+
_expiryTimer = null;
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
},
|
|
1138
|
+
/**
|
|
1139
|
+
* Fired on TERMINAL 401 — after the refresh+retry path is exhausted.
|
|
1140
|
+
* The store has already cleared the session before calling back, so
|
|
1141
|
+
* handlers only need to route to login (no clear-then-redirect
|
|
1142
|
+
* ordering to get wrong). Returns an unsubscribe function; multiple
|
|
1143
|
+
* handlers compose (unlike the legacy single-slot `onUnauthorized`).
|
|
1144
|
+
*/
|
|
1145
|
+
onSessionExpired(cb) {
|
|
1146
|
+
_sessionExpiredHandlers.add(cb);
|
|
1147
|
+
return () => {
|
|
1148
|
+
_sessionExpiredHandlers.delete(cb);
|
|
1149
|
+
};
|
|
1006
1150
|
},
|
|
1007
1151
|
// ── API key ───────────────────────────────────────────────────────
|
|
1008
1152
|
getApiKey() {
|
|
@@ -1084,15 +1228,22 @@ var auth = {
|
|
|
1084
1228
|
async function tryRefresh() {
|
|
1085
1229
|
if (_refreshInflight) return _refreshInflight;
|
|
1086
1230
|
if (!_refreshHandler) return null;
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1231
|
+
const runRefresh = /* @__PURE__ */ __name(async () => {
|
|
1232
|
+
const refresh = auth.getRefreshToken();
|
|
1233
|
+
if (!refresh) return null;
|
|
1234
|
+
const result = await _refreshHandler(refresh);
|
|
1235
|
+
if (!result?.access) return null;
|
|
1236
|
+
auth.setToken(result.access);
|
|
1237
|
+
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
1238
|
+
return result.access;
|
|
1239
|
+
}, "runRefresh");
|
|
1089
1240
|
_refreshInflight = (async () => {
|
|
1090
1241
|
try {
|
|
1091
|
-
const
|
|
1092
|
-
if (
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return
|
|
1242
|
+
const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
|
|
1243
|
+
if (locks?.request) {
|
|
1244
|
+
return await locks.request(`${REFRESH_KEY}:refresh`, runRefresh);
|
|
1245
|
+
}
|
|
1246
|
+
return await runRefresh();
|
|
1096
1247
|
} catch {
|
|
1097
1248
|
return null;
|
|
1098
1249
|
} finally {
|
|
@@ -1102,6 +1253,22 @@ async function tryRefresh() {
|
|
|
1102
1253
|
return _refreshInflight;
|
|
1103
1254
|
}
|
|
1104
1255
|
__name(tryRefresh, "tryRefresh");
|
|
1256
|
+
function expireSession(response) {
|
|
1257
|
+
auth.clearTokens();
|
|
1258
|
+
for (const cb of Array.from(_sessionExpiredHandlers)) {
|
|
1259
|
+
try {
|
|
1260
|
+
cb(response);
|
|
1261
|
+
} catch {
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
if (_onUnauthorized) {
|
|
1265
|
+
try {
|
|
1266
|
+
_onUnauthorized(response);
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
__name(expireSession, "expireSession");
|
|
1105
1272
|
function dpopEnabled() {
|
|
1106
1273
|
try {
|
|
1107
1274
|
return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DPOP_ENABLED === "true";
|
|
@@ -1231,22 +1398,12 @@ function installAuthOnClient(client2) {
|
|
|
1231
1398
|
client2.interceptors.response.use(async (response, request) => {
|
|
1232
1399
|
if (response.status !== 401) return response;
|
|
1233
1400
|
if (request.headers.get(RETRY_MARKER)) {
|
|
1234
|
-
|
|
1235
|
-
try {
|
|
1236
|
-
_onUnauthorized(response);
|
|
1237
|
-
} catch {
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1401
|
+
expireSession(response);
|
|
1240
1402
|
return response;
|
|
1241
1403
|
}
|
|
1242
1404
|
const newToken = await tryRefresh();
|
|
1243
1405
|
if (!newToken) {
|
|
1244
|
-
|
|
1245
|
-
try {
|
|
1246
|
-
_onUnauthorized(response);
|
|
1247
|
-
} catch {
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1406
|
+
expireSession(response);
|
|
1250
1407
|
return response;
|
|
1251
1408
|
}
|
|
1252
1409
|
const retry = request.clone();
|
|
@@ -1258,25 +1415,36 @@ function installAuthOnClient(client2) {
|
|
|
1258
1415
|
}
|
|
1259
1416
|
try {
|
|
1260
1417
|
const retried = await fetch(retry);
|
|
1261
|
-
if (retried.status === 401
|
|
1262
|
-
try {
|
|
1263
|
-
_onUnauthorized(retried);
|
|
1264
|
-
} catch {
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1418
|
+
if (retried.status === 401) expireSession(retried);
|
|
1267
1419
|
return retried;
|
|
1268
1420
|
} catch {
|
|
1269
|
-
if (_onUnauthorized) {
|
|
1270
|
-
try {
|
|
1271
|
-
_onUnauthorized(response);
|
|
1272
|
-
} catch {
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
1421
|
return response;
|
|
1276
1422
|
}
|
|
1277
1423
|
});
|
|
1278
1424
|
}
|
|
1279
1425
|
__name(installAuthOnClient, "installAuthOnClient");
|
|
1426
|
+
var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
|
|
1427
|
+
auth.setRefreshHandler(async (refresh) => {
|
|
1428
|
+
try {
|
|
1429
|
+
const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
|
|
1430
|
+
const headers = { "Content-Type": "application/json" };
|
|
1431
|
+
if (dpopEnabled() && typeof window !== "undefined") {
|
|
1432
|
+
const proof = await _makeDpopProof("POST", url);
|
|
1433
|
+
if (proof) headers["DPoP"] = proof;
|
|
1434
|
+
}
|
|
1435
|
+
const res = await fetch(url, {
|
|
1436
|
+
method: "POST",
|
|
1437
|
+
headers,
|
|
1438
|
+
credentials: auth.getWithCredentials() ? "include" : "same-origin",
|
|
1439
|
+
body: JSON.stringify({ refresh })
|
|
1440
|
+
});
|
|
1441
|
+
if (!res.ok) return null;
|
|
1442
|
+
const data = await res.json();
|
|
1443
|
+
return data?.access ? { access: data.access, refresh: data.refresh ?? refresh } : null;
|
|
1444
|
+
} catch {
|
|
1445
|
+
return null;
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1280
1448
|
|
|
1281
1449
|
// src/_api/generated/client.gen.ts
|
|
1282
1450
|
var client = createClient(createConfig({ baseUrl: "http://localhost:8000" }));
|