@djangocfg/api 2.1.619 → 2.1.622
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 +441 -106
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +80 -2
- package/dist/auth.d.ts +80 -2
- package/dist/auth.mjs +396 -63
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +43 -6
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.mjs +43 -6
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +43 -6
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +43 -6
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +264 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +264 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/_api/generated/helpers/auth.ts +33 -6
- package/src/auth/__tests__/authState.test.ts +46 -0
- package/src/auth/__tests__/formatAuthError.test.ts +7 -0
- package/src/auth/__tests__/sharedContext.test.ts +16 -0
- package/src/auth/__tests__/trace.test.ts +38 -0
- package/src/auth/context/AccountsContext.tsx +6 -2
- package/src/auth/context/AuthContext.tsx +63 -10
- package/src/auth/context/sharedContext.ts +16 -0
- package/src/auth/context/types.ts +2 -0
- package/src/auth/hooks/useCfgRouter.ts +31 -0
- package/src/auth/hooks/useOAuthLogin.ts +3 -0
- package/src/auth/hooks/useTwoFactorVerify.ts +2 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/trace.ts +253 -0
- package/src/auth/utils/authState.ts +39 -0
- package/src/auth/utils/errors.ts +5 -2
- package/src/auth/utils/index.ts +1 -0
- package/src/index.ts +3 -0
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,193 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
+
// src/auth/trace.ts
|
|
5
|
+
var KEY = "cfg.auth.trace";
|
|
6
|
+
var MAX = 200;
|
|
7
|
+
var MAX_STRING = 120;
|
|
8
|
+
var SENSITIVE = /token|otp|code|email|password|secret|cookie|authorization|key|identifier/i;
|
|
9
|
+
var TAB = (() => {
|
|
10
|
+
const g = globalThis;
|
|
11
|
+
return g.__cfgAuthTab ??= Math.random().toString(36).slice(2, 6);
|
|
12
|
+
})();
|
|
13
|
+
var hasWindow = typeof window !== "undefined";
|
|
14
|
+
var memory = [];
|
|
15
|
+
var loaded = false;
|
|
16
|
+
function backend() {
|
|
17
|
+
if (!hasWindow) return null;
|
|
18
|
+
for (const name of ["localStorage", "sessionStorage"]) {
|
|
19
|
+
try {
|
|
20
|
+
const s = window[name];
|
|
21
|
+
s.setItem("__cfg_probe", "1");
|
|
22
|
+
s.removeItem("__cfg_probe");
|
|
23
|
+
return s;
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
__name(backend, "backend");
|
|
30
|
+
function load() {
|
|
31
|
+
if (loaded) return memory;
|
|
32
|
+
loaded = true;
|
|
33
|
+
try {
|
|
34
|
+
const raw = backend()?.getItem(KEY);
|
|
35
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
36
|
+
if (Array.isArray(parsed)) memory = parsed.filter((e) => typeof e === "object" && e !== null);
|
|
37
|
+
} catch {
|
|
38
|
+
memory = [];
|
|
39
|
+
}
|
|
40
|
+
return memory;
|
|
41
|
+
}
|
|
42
|
+
__name(load, "load");
|
|
43
|
+
function persist() {
|
|
44
|
+
try {
|
|
45
|
+
backend()?.setItem(KEY, JSON.stringify(memory));
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
__name(persist, "persist");
|
|
50
|
+
function clean(fields) {
|
|
51
|
+
if (!fields) return void 0;
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const [k2, v] of Object.entries(fields)) {
|
|
54
|
+
if (SENSITIVE.test(k2)) continue;
|
|
55
|
+
if (typeof v === "string") out[k2] = v.length > MAX_STRING ? `${v.slice(0, MAX_STRING)}\u2026` : v;
|
|
56
|
+
else if (typeof v === "number" || typeof v === "boolean" || v === null) out[k2] = v;
|
|
57
|
+
}
|
|
58
|
+
return Object.keys(out).length ? out : void 0;
|
|
59
|
+
}
|
|
60
|
+
__name(clean, "clean");
|
|
61
|
+
function refresh() {
|
|
62
|
+
try {
|
|
63
|
+
const raw = backend()?.getItem(KEY);
|
|
64
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
65
|
+
if (Array.isArray(parsed)) memory = parsed.filter((e) => typeof e === "object" && e !== null);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
__name(refresh, "refresh");
|
|
70
|
+
function record(at, ev, fields) {
|
|
71
|
+
load();
|
|
72
|
+
refresh();
|
|
73
|
+
const entries = memory;
|
|
74
|
+
const p = hasWindow && typeof performance !== "undefined" ? Math.round(performance.now()) : 0;
|
|
75
|
+
const f2 = clean(fields);
|
|
76
|
+
const last = entries[entries.length - 1];
|
|
77
|
+
if (last && last.tab === TAB && last.ev === ev && JSON.stringify(last.f) === JSON.stringify(f2)) {
|
|
78
|
+
last.n = (last.n ?? 1) + 1;
|
|
79
|
+
last.t = at;
|
|
80
|
+
last.p = p;
|
|
81
|
+
persist();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
entries.push({ t: at, p, tab: TAB, ev, f: f2 });
|
|
85
|
+
if (entries.length > MAX) entries.splice(0, entries.length - MAX);
|
|
86
|
+
persist();
|
|
87
|
+
}
|
|
88
|
+
__name(record, "record");
|
|
89
|
+
function authTrace(ev, fields) {
|
|
90
|
+
if (!hasWindow) return;
|
|
91
|
+
try {
|
|
92
|
+
record(Date.now(), ev, fields);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
__name(authTrace, "authTrace");
|
|
97
|
+
function readAuthTrace() {
|
|
98
|
+
return load().slice();
|
|
99
|
+
}
|
|
100
|
+
__name(readAuthTrace, "readAuthTrace");
|
|
101
|
+
function clearAuthTrace() {
|
|
102
|
+
memory = [];
|
|
103
|
+
loaded = true;
|
|
104
|
+
try {
|
|
105
|
+
backend()?.removeItem(KEY);
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
__name(clearAuthTrace, "clearAuthTrace");
|
|
110
|
+
function context() {
|
|
111
|
+
if (!hasWindow) return [];
|
|
112
|
+
const copies = globalThis.__cfgAuthCopies ?? 1;
|
|
113
|
+
const lines = [`url: ${window.location.href.split("?")[0]}`, `copies: ${String(copies)}`, `online: ${navigator.onLine}`, `ua: ${navigator.userAgent}`];
|
|
114
|
+
try {
|
|
115
|
+
const auth2 = globalThis.__cfgAuthNow;
|
|
116
|
+
const now = auth2?.();
|
|
117
|
+
if (now) lines.push(`now: ${Object.entries(now).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}`);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
return lines;
|
|
121
|
+
}
|
|
122
|
+
__name(context, "context");
|
|
123
|
+
function dumpAuthTrace() {
|
|
124
|
+
const entries = load();
|
|
125
|
+
const first = entries[0]?.t ?? Date.now();
|
|
126
|
+
const rows = entries.map((e) => {
|
|
127
|
+
const fields = e.f ? ` ${Object.entries(e.f).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}` : "";
|
|
128
|
+
const at = new Date(e.t).toISOString().slice(11, 23);
|
|
129
|
+
return `${at} +${String(e.t - first).padStart(6)}ms [${e.tab} p${e.p}] ${e.ev}${fields}${e.n && e.n > 1 ? ` x${e.n}` : ""}`;
|
|
130
|
+
});
|
|
131
|
+
return [`# cfg auth trace, ${entries.length} events`, ...context(), "", ...rows].join("\n");
|
|
132
|
+
}
|
|
133
|
+
__name(dumpAuthTrace, "dumpAuthTrace");
|
|
134
|
+
function installAuthTrace() {
|
|
135
|
+
if (!hasWindow) return;
|
|
136
|
+
const g = globalThis;
|
|
137
|
+
g.__cfgAuthTrace = authTrace;
|
|
138
|
+
g.__cfgAuth = {
|
|
139
|
+
trace: readAuthTrace,
|
|
140
|
+
dump: /* @__PURE__ */ __name(() => {
|
|
141
|
+
const text = dumpAuthTrace();
|
|
142
|
+
console.log(text);
|
|
143
|
+
return text;
|
|
144
|
+
}, "dump"),
|
|
145
|
+
clear: clearAuthTrace,
|
|
146
|
+
/** Put the dump on the clipboard (works when the console output is blocked or too long to select). */
|
|
147
|
+
copy: /* @__PURE__ */ __name(() => {
|
|
148
|
+
const text = dumpAuthTrace();
|
|
149
|
+
void navigator.clipboard?.writeText(text);
|
|
150
|
+
return `${text.split("\n").length} lines copied`;
|
|
151
|
+
}, "copy"),
|
|
152
|
+
/** Download the raw trace as a JSON file. */
|
|
153
|
+
download: /* @__PURE__ */ __name(() => {
|
|
154
|
+
const blob = new Blob([JSON.stringify(readAuthTrace(), null, 1)], { type: "application/json" });
|
|
155
|
+
const a = document.createElement("a");
|
|
156
|
+
a.href = URL.createObjectURL(blob);
|
|
157
|
+
a.download = `auth-trace-${Date.now()}.json`;
|
|
158
|
+
a.click();
|
|
159
|
+
}, "download")
|
|
160
|
+
};
|
|
161
|
+
const queued = g.__cfgAuthTraceQ;
|
|
162
|
+
if (Array.isArray(queued)) {
|
|
163
|
+
g.__cfgAuthTraceQ = [];
|
|
164
|
+
for (const item of queued) {
|
|
165
|
+
if (Array.isArray(item) && typeof item[1] === "string") {
|
|
166
|
+
try {
|
|
167
|
+
record(Number(item[0]) || Date.now(), item[1], item[2]);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
__name(installAuthTrace, "installAuthTrace");
|
|
175
|
+
function registerCopy() {
|
|
176
|
+
if (!hasWindow) return;
|
|
177
|
+
const g = globalThis;
|
|
178
|
+
const n = (g.__cfgAuthCopies ?? 0) + 1;
|
|
179
|
+
g.__cfgAuthCopies = n;
|
|
180
|
+
if (n > 1) {
|
|
181
|
+
authTrace("dup.copy", { copies: n });
|
|
182
|
+
if (n === 2) {
|
|
183
|
+
console.warn("[@djangocfg/api] more than one copy of the auth package is loaded; see __cfgAuth.dump() (dup.copy). Align the peer set so pnpm installs one instance.");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
__name(registerCopy, "registerCopy");
|
|
188
|
+
registerCopy();
|
|
189
|
+
installAuthTrace();
|
|
190
|
+
|
|
4
191
|
// src/_api/generated/helpers/errors.ts
|
|
5
192
|
var APIError = class extends Error {
|
|
6
193
|
constructor(statusCode, statusText, response, url, message) {
|
|
@@ -117,10 +304,10 @@ function normalizeSegment(raw) {
|
|
|
117
304
|
return raw.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
|
|
118
305
|
}
|
|
119
306
|
__name(normalizeSegment, "normalizeSegment");
|
|
120
|
-
var
|
|
307
|
+
var hasWindow2 = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
|
|
121
308
|
var domainByHost = /* @__PURE__ */ new Map();
|
|
122
309
|
function detectCookieDomain() {
|
|
123
|
-
if (!
|
|
310
|
+
if (!hasWindow2() || typeof document === "undefined") return null;
|
|
124
311
|
const host = window.location.hostname.toLowerCase();
|
|
125
312
|
if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
|
|
126
313
|
const cached = domainByHost.get(host);
|
|
@@ -144,7 +331,7 @@ function detectCookieDomain() {
|
|
|
144
331
|
}
|
|
145
332
|
__name(detectCookieDomain, "detectCookieDomain");
|
|
146
333
|
function nativeStorage(store) {
|
|
147
|
-
if (!
|
|
334
|
+
if (!hasWindow2()) return null;
|
|
148
335
|
try {
|
|
149
336
|
return store === "session" ? window.sessionStorage : window.localStorage;
|
|
150
337
|
} catch {
|
|
@@ -153,7 +340,7 @@ function nativeStorage(store) {
|
|
|
153
340
|
}
|
|
154
341
|
__name(nativeStorage, "nativeStorage");
|
|
155
342
|
function cookieNames() {
|
|
156
|
-
if (!
|
|
343
|
+
if (!hasWindow2()) return [];
|
|
157
344
|
try {
|
|
158
345
|
return document.cookie.split("; ").map((part) => decodeURIComponent(part.split("=")[0] ?? "")).filter(Boolean);
|
|
159
346
|
} catch {
|
|
@@ -178,7 +365,7 @@ function createStorage(owner, options = {}) {
|
|
|
178
365
|
const families = Object.values(options.families ?? {});
|
|
179
366
|
const specByKey = /* @__PURE__ */ new Map();
|
|
180
367
|
for (const spec of specs) specByKey.set(spec.key, [...specByKey.get(spec.key) ?? [], spec]);
|
|
181
|
-
const
|
|
368
|
+
const memory2 = {
|
|
182
369
|
local: /* @__PURE__ */ new Map(),
|
|
183
370
|
session: /* @__PURE__ */ new Map(),
|
|
184
371
|
cookie: /* @__PURE__ */ new Map()
|
|
@@ -192,7 +379,7 @@ function createStorage(owner, options = {}) {
|
|
|
192
379
|
if (!key.startsWith(prefix)) throw new TypeError(`storage key "${key}" is not owned by "${owner}"`);
|
|
193
380
|
}, "assertOwned");
|
|
194
381
|
function rawGet(store, key) {
|
|
195
|
-
if (!
|
|
382
|
+
if (!hasWindow2()) return null;
|
|
196
383
|
if (store === "cookie") {
|
|
197
384
|
try {
|
|
198
385
|
const escaped = encodeURIComponent(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -200,18 +387,18 @@ function createStorage(owner, options = {}) {
|
|
|
200
387
|
if (match?.[1] !== void 0) return decodeURIComponent(match[1]);
|
|
201
388
|
} catch {
|
|
202
389
|
}
|
|
203
|
-
return
|
|
390
|
+
return memory2.cookie.get(key) ?? null;
|
|
204
391
|
}
|
|
205
392
|
try {
|
|
206
393
|
const value = nativeStorage(store)?.getItem(key) ?? null;
|
|
207
394
|
if (value !== null) return value;
|
|
208
395
|
} catch {
|
|
209
396
|
}
|
|
210
|
-
return
|
|
397
|
+
return memory2[store].get(key) ?? null;
|
|
211
398
|
}
|
|
212
399
|
__name(rawGet, "rawGet");
|
|
213
400
|
function rawSet(store, key, value, maxAgeSeconds) {
|
|
214
|
-
if (!
|
|
401
|
+
if (!hasWindow2()) return false;
|
|
215
402
|
try {
|
|
216
403
|
if (store === "cookie") {
|
|
217
404
|
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
@@ -219,24 +406,24 @@ function createStorage(owner, options = {}) {
|
|
|
219
406
|
const domainPart = domain ? `; Domain=${domain}` : "";
|
|
220
407
|
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
|
|
221
408
|
if (domain) expireCookie(key, null);
|
|
222
|
-
if (rawGet("cookie", key) === value && !
|
|
223
|
-
|
|
409
|
+
if (rawGet("cookie", key) === value && !memory2.cookie.has(key)) return true;
|
|
410
|
+
memory2.cookie.set(key, value);
|
|
224
411
|
return false;
|
|
225
412
|
}
|
|
226
413
|
const native = nativeStorage(store);
|
|
227
414
|
if (!native) throw new Error("storage unavailable");
|
|
228
415
|
native.setItem(key, value);
|
|
229
|
-
|
|
416
|
+
memory2[store].delete(key);
|
|
230
417
|
return true;
|
|
231
418
|
} catch {
|
|
232
|
-
|
|
419
|
+
memory2[store].set(key, value);
|
|
233
420
|
return false;
|
|
234
421
|
}
|
|
235
422
|
}
|
|
236
423
|
__name(rawSet, "rawSet");
|
|
237
424
|
function rawRemove(store, key) {
|
|
238
|
-
|
|
239
|
-
if (!
|
|
425
|
+
memory2[store].delete(key);
|
|
426
|
+
if (!hasWindow2()) return;
|
|
240
427
|
try {
|
|
241
428
|
if (store === "cookie") {
|
|
242
429
|
expireCookie(key, null);
|
|
@@ -265,7 +452,7 @@ function createStorage(owner, options = {}) {
|
|
|
265
452
|
}
|
|
266
453
|
} catch {
|
|
267
454
|
}
|
|
268
|
-
for (const name of
|
|
455
|
+
for (const name of memory2[store].keys()) if (name.startsWith(startsWith) && !out.includes(name)) out.push(name);
|
|
269
456
|
return out;
|
|
270
457
|
}
|
|
271
458
|
__name(listKeys, "listKeys");
|
|
@@ -337,7 +524,7 @@ function createStorage(owner, options = {}) {
|
|
|
337
524
|
cookieDomain: resolveCookieDomain,
|
|
338
525
|
onChange(key, listener, store = "local") {
|
|
339
526
|
assertValid(key);
|
|
340
|
-
if (!
|
|
527
|
+
if (!hasWindow2()) return () => {
|
|
341
528
|
};
|
|
342
529
|
const handler = /* @__PURE__ */ __name((event) => {
|
|
343
530
|
if (event.storageArea !== nativeStorage(store)) return;
|
|
@@ -361,6 +548,29 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
|
|
|
361
548
|
var API_KEY_KEY = "cfg.auth.api_key";
|
|
362
549
|
var storage = true ? cfgStorage : createStorage2("cfg");
|
|
363
550
|
var isBrowser = typeof window !== "undefined";
|
|
551
|
+
function trace(ev, fields) {
|
|
552
|
+
try {
|
|
553
|
+
const g = globalThis;
|
|
554
|
+
const sink = g.__cfgAuthTrace;
|
|
555
|
+
if (sink) {
|
|
556
|
+
sink(ev, fields);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
const queue = g.__cfgAuthTraceQ ?? [];
|
|
560
|
+
g.__cfgAuthTraceQ = queue;
|
|
561
|
+
if (queue.length < 100) queue.push([Date.now(), ev, fields]);
|
|
562
|
+
} catch {
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
__name(trace, "trace");
|
|
566
|
+
function pathOf(url) {
|
|
567
|
+
try {
|
|
568
|
+
return new URL(url, "http://x").pathname;
|
|
569
|
+
} catch {
|
|
570
|
+
return "";
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
__name(pathOf, "pathOf");
|
|
364
574
|
function secondsUntilExp(token) {
|
|
365
575
|
const exp = jwtExpMs(token);
|
|
366
576
|
return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
|
|
@@ -391,10 +601,10 @@ function adoptLegacySession() {
|
|
|
391
601
|
if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
|
|
392
602
|
const legacy = backendFor("localStorage");
|
|
393
603
|
const access = legacy.get(ACCESS_KEY);
|
|
394
|
-
const
|
|
395
|
-
if (access === null &&
|
|
604
|
+
const refresh2 = legacy.get(REFRESH_KEY);
|
|
605
|
+
if (access === null && refresh2 === null) return;
|
|
396
606
|
if (access !== null) _storage.set(ACCESS_KEY, access);
|
|
397
|
-
if (
|
|
607
|
+
if (refresh2 !== null) _storage.set(REFRESH_KEY, refresh2);
|
|
398
608
|
legacy.set(ACCESS_KEY, null);
|
|
399
609
|
legacy.set(REFRESH_KEY, null);
|
|
400
610
|
} catch {
|
|
@@ -479,12 +689,12 @@ function jwtExpMs(token) {
|
|
|
479
689
|
__name(jwtExpMs, "jwtExpMs");
|
|
480
690
|
function computeSnapshot() {
|
|
481
691
|
const access = _storage.get(ACCESS_KEY);
|
|
482
|
-
const
|
|
692
|
+
const refresh2 = _storage.get(REFRESH_KEY);
|
|
483
693
|
const now = Date.now();
|
|
484
694
|
const accessExp = access ? jwtExpMs(access) : null;
|
|
485
695
|
const accessAlive = access !== null && (accessExp === null || accessExp > now);
|
|
486
|
-
const refreshExp =
|
|
487
|
-
const refreshAlive =
|
|
696
|
+
const refreshExp = refresh2 ? jwtExpMs(refresh2) : null;
|
|
697
|
+
const refreshAlive = refresh2 !== null && (refreshExp === null || refreshExp > now);
|
|
488
698
|
return {
|
|
489
699
|
status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
|
|
490
700
|
accessExpiresAt: accessExp
|
|
@@ -507,9 +717,9 @@ function scheduleExpiryFlip() {
|
|
|
507
717
|
const now = Date.now();
|
|
508
718
|
const exps = [];
|
|
509
719
|
const access = _storage.get(ACCESS_KEY);
|
|
510
|
-
const
|
|
720
|
+
const refresh2 = _storage.get(REFRESH_KEY);
|
|
511
721
|
const accessExp = access ? jwtExpMs(access) : null;
|
|
512
|
-
const refreshExp =
|
|
722
|
+
const refreshExp = refresh2 ? jwtExpMs(refresh2) : null;
|
|
513
723
|
if (accessExp !== null && accessExp > now) exps.push(accessExp);
|
|
514
724
|
if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
|
|
515
725
|
if (!exps.length) return;
|
|
@@ -520,7 +730,10 @@ __name(scheduleExpiryFlip, "scheduleExpiryFlip");
|
|
|
520
730
|
function notifySessionChanged() {
|
|
521
731
|
const next = computeSnapshot();
|
|
522
732
|
const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
|
|
523
|
-
if (changed)
|
|
733
|
+
if (changed) {
|
|
734
|
+
_snapshot = next;
|
|
735
|
+
trace("store.snapshot", { status: next.status });
|
|
736
|
+
}
|
|
524
737
|
scheduleExpiryFlip();
|
|
525
738
|
if (!changed) return;
|
|
526
739
|
for (const listener of Array.from(_sessionListeners)) {
|
|
@@ -603,6 +816,7 @@ var auth = {
|
|
|
603
816
|
return _storage.get(ACCESS_KEY);
|
|
604
817
|
},
|
|
605
818
|
setToken(token) {
|
|
819
|
+
trace("store.setToken", { present: token !== null });
|
|
606
820
|
_storage.set(ACCESS_KEY, token);
|
|
607
821
|
notifySessionChanged();
|
|
608
822
|
},
|
|
@@ -610,14 +824,16 @@ var auth = {
|
|
|
610
824
|
return _storage.get(REFRESH_KEY);
|
|
611
825
|
},
|
|
612
826
|
setRefreshToken(token) {
|
|
827
|
+
trace("store.setRefreshToken", { present: token !== null });
|
|
613
828
|
_storage.set(REFRESH_KEY, token);
|
|
614
829
|
notifySessionChanged();
|
|
615
830
|
},
|
|
616
831
|
clearTokens() {
|
|
832
|
+
trace("store.clearTokens");
|
|
617
833
|
for (const mode of ["localStorage", "sessionStorage", "cookie"]) {
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
834
|
+
const backend2 = backendFor(mode);
|
|
835
|
+
backend2.set(ACCESS_KEY, null);
|
|
836
|
+
backend2.set(REFRESH_KEY, null);
|
|
621
837
|
}
|
|
622
838
|
storage.clearAll();
|
|
623
839
|
notifySessionChanged();
|
|
@@ -636,6 +852,7 @@ var auth = {
|
|
|
636
852
|
* rotation); `refresh: null` explicitly drops it.
|
|
637
853
|
*/
|
|
638
854
|
setSession(tokens) {
|
|
855
|
+
trace("store.setSession", { refresh: tokens.refresh === void 0 ? "kept" : tokens.refresh === null ? "dropped" : "set" });
|
|
639
856
|
_storage.set(ACCESS_KEY, tokens.access);
|
|
640
857
|
if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
|
|
641
858
|
notifySessionChanged();
|
|
@@ -771,10 +988,14 @@ async function tryRefresh() {
|
|
|
771
988
|
if (_refreshInflight) return _refreshInflight;
|
|
772
989
|
if (!_refreshHandler) return null;
|
|
773
990
|
const runRefresh = /* @__PURE__ */ __name(async () => {
|
|
774
|
-
const
|
|
775
|
-
if (!
|
|
776
|
-
const result = await _refreshHandler(
|
|
777
|
-
if (!result?.access)
|
|
991
|
+
const refresh2 = auth.getRefreshToken();
|
|
992
|
+
if (!refresh2) return null;
|
|
993
|
+
const result = await _refreshHandler(refresh2);
|
|
994
|
+
if (!result?.access) {
|
|
995
|
+
trace("refresh", { ok: false, why: "no-access" });
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
trace("refresh", { ok: true, rotated: Boolean(result.refresh) });
|
|
778
999
|
auth.setToken(result.access);
|
|
779
1000
|
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
780
1001
|
return result.access;
|
|
@@ -787,6 +1008,7 @@ async function tryRefresh() {
|
|
|
787
1008
|
}
|
|
788
1009
|
return await runRefresh();
|
|
789
1010
|
} catch {
|
|
1011
|
+
trace("refresh", { ok: false, why: "threw" });
|
|
790
1012
|
return null;
|
|
791
1013
|
} finally {
|
|
792
1014
|
_refreshInflight = null;
|
|
@@ -795,7 +1017,8 @@ async function tryRefresh() {
|
|
|
795
1017
|
return _refreshInflight;
|
|
796
1018
|
}
|
|
797
1019
|
__name(tryRefresh, "tryRefresh");
|
|
798
|
-
function expireSession(response) {
|
|
1020
|
+
function expireSession(response, why) {
|
|
1021
|
+
trace("store.expire", { why, status: response.status, path: pathOf(response.url), handlers: _sessionExpiredHandlers.size });
|
|
799
1022
|
auth.clearTokens();
|
|
800
1023
|
for (const cb of Array.from(_sessionExpiredHandlers)) {
|
|
801
1024
|
try {
|
|
@@ -938,15 +1161,16 @@ function installAuthOnClient(client2) {
|
|
|
938
1161
|
client2.interceptors.response.use(async (response, request) => {
|
|
939
1162
|
if (response.status !== 401) return response;
|
|
940
1163
|
if (/\/accounts\/(otp|oauth|login|register|token)\b/.test(request.url)) {
|
|
1164
|
+
trace("401.passthrough", { path: pathOf(request.url) });
|
|
941
1165
|
return response;
|
|
942
1166
|
}
|
|
943
1167
|
if (request.headers.get(RETRY_MARKER)) {
|
|
944
|
-
expireSession(response);
|
|
1168
|
+
expireSession(response, "retry-marker");
|
|
945
1169
|
return response;
|
|
946
1170
|
}
|
|
947
1171
|
const newToken = await tryRefresh();
|
|
948
1172
|
if (!newToken) {
|
|
949
|
-
expireSession(response);
|
|
1173
|
+
expireSession(response, "refresh-failed");
|
|
950
1174
|
return response;
|
|
951
1175
|
}
|
|
952
1176
|
const retry = request.clone();
|
|
@@ -958,7 +1182,7 @@ function installAuthOnClient(client2) {
|
|
|
958
1182
|
}
|
|
959
1183
|
try {
|
|
960
1184
|
const retried = await fetch(retry);
|
|
961
|
-
if (retried.status === 401) expireSession(retried);
|
|
1185
|
+
if (retried.status === 401) expireSession(retried, "retry-401");
|
|
962
1186
|
return retried;
|
|
963
1187
|
} catch {
|
|
964
1188
|
return response;
|
|
@@ -967,7 +1191,7 @@ function installAuthOnClient(client2) {
|
|
|
967
1191
|
}
|
|
968
1192
|
__name(installAuthOnClient, "installAuthOnClient");
|
|
969
1193
|
var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
|
|
970
|
-
auth.setRefreshHandler(async (
|
|
1194
|
+
auth.setRefreshHandler(async (refresh2) => {
|
|
971
1195
|
try {
|
|
972
1196
|
const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
|
|
973
1197
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -979,11 +1203,11 @@ auth.setRefreshHandler(async (refresh) => {
|
|
|
979
1203
|
method: "POST",
|
|
980
1204
|
headers,
|
|
981
1205
|
credentials: auth.getWithCredentials() ? "include" : "same-origin",
|
|
982
|
-
body: JSON.stringify({ refresh })
|
|
1206
|
+
body: JSON.stringify({ refresh: refresh2 })
|
|
983
1207
|
});
|
|
984
1208
|
if (!res.ok) return null;
|
|
985
1209
|
const data = await res.json();
|
|
986
|
-
return data?.access ? { access: data.access, refresh: data.refresh ??
|
|
1210
|
+
return data?.access ? { access: data.access, refresh: data.refresh ?? refresh2 } : null;
|
|
987
1211
|
} catch {
|
|
988
1212
|
return null;
|
|
989
1213
|
}
|