@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.cjs
CHANGED
|
@@ -64,6 +64,193 @@ __export(index_exports, {
|
|
|
64
64
|
});
|
|
65
65
|
module.exports = __toCommonJS(index_exports);
|
|
66
66
|
|
|
67
|
+
// src/auth/trace.ts
|
|
68
|
+
var KEY = "cfg.auth.trace";
|
|
69
|
+
var MAX = 200;
|
|
70
|
+
var MAX_STRING = 120;
|
|
71
|
+
var SENSITIVE = /token|otp|code|email|password|secret|cookie|authorization|key|identifier/i;
|
|
72
|
+
var TAB = (() => {
|
|
73
|
+
const g = globalThis;
|
|
74
|
+
return g.__cfgAuthTab ??= Math.random().toString(36).slice(2, 6);
|
|
75
|
+
})();
|
|
76
|
+
var hasWindow = typeof window !== "undefined";
|
|
77
|
+
var memory = [];
|
|
78
|
+
var loaded = false;
|
|
79
|
+
function backend() {
|
|
80
|
+
if (!hasWindow) return null;
|
|
81
|
+
for (const name of ["localStorage", "sessionStorage"]) {
|
|
82
|
+
try {
|
|
83
|
+
const s = window[name];
|
|
84
|
+
s.setItem("__cfg_probe", "1");
|
|
85
|
+
s.removeItem("__cfg_probe");
|
|
86
|
+
return s;
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
__name(backend, "backend");
|
|
93
|
+
function load() {
|
|
94
|
+
if (loaded) return memory;
|
|
95
|
+
loaded = true;
|
|
96
|
+
try {
|
|
97
|
+
const raw = backend()?.getItem(KEY);
|
|
98
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
99
|
+
if (Array.isArray(parsed)) memory = parsed.filter((e) => typeof e === "object" && e !== null);
|
|
100
|
+
} catch {
|
|
101
|
+
memory = [];
|
|
102
|
+
}
|
|
103
|
+
return memory;
|
|
104
|
+
}
|
|
105
|
+
__name(load, "load");
|
|
106
|
+
function persist() {
|
|
107
|
+
try {
|
|
108
|
+
backend()?.setItem(KEY, JSON.stringify(memory));
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
__name(persist, "persist");
|
|
113
|
+
function clean(fields) {
|
|
114
|
+
if (!fields) return void 0;
|
|
115
|
+
const out = {};
|
|
116
|
+
for (const [k2, v] of Object.entries(fields)) {
|
|
117
|
+
if (SENSITIVE.test(k2)) continue;
|
|
118
|
+
if (typeof v === "string") out[k2] = v.length > MAX_STRING ? `${v.slice(0, MAX_STRING)}\u2026` : v;
|
|
119
|
+
else if (typeof v === "number" || typeof v === "boolean" || v === null) out[k2] = v;
|
|
120
|
+
}
|
|
121
|
+
return Object.keys(out).length ? out : void 0;
|
|
122
|
+
}
|
|
123
|
+
__name(clean, "clean");
|
|
124
|
+
function refresh() {
|
|
125
|
+
try {
|
|
126
|
+
const raw = backend()?.getItem(KEY);
|
|
127
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
128
|
+
if (Array.isArray(parsed)) memory = parsed.filter((e) => typeof e === "object" && e !== null);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
__name(refresh, "refresh");
|
|
133
|
+
function record(at, ev, fields) {
|
|
134
|
+
load();
|
|
135
|
+
refresh();
|
|
136
|
+
const entries = memory;
|
|
137
|
+
const p = hasWindow && typeof performance !== "undefined" ? Math.round(performance.now()) : 0;
|
|
138
|
+
const f2 = clean(fields);
|
|
139
|
+
const last = entries[entries.length - 1];
|
|
140
|
+
if (last && last.tab === TAB && last.ev === ev && JSON.stringify(last.f) === JSON.stringify(f2)) {
|
|
141
|
+
last.n = (last.n ?? 1) + 1;
|
|
142
|
+
last.t = at;
|
|
143
|
+
last.p = p;
|
|
144
|
+
persist();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
entries.push({ t: at, p, tab: TAB, ev, f: f2 });
|
|
148
|
+
if (entries.length > MAX) entries.splice(0, entries.length - MAX);
|
|
149
|
+
persist();
|
|
150
|
+
}
|
|
151
|
+
__name(record, "record");
|
|
152
|
+
function authTrace(ev, fields) {
|
|
153
|
+
if (!hasWindow) return;
|
|
154
|
+
try {
|
|
155
|
+
record(Date.now(), ev, fields);
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
__name(authTrace, "authTrace");
|
|
160
|
+
function readAuthTrace() {
|
|
161
|
+
return load().slice();
|
|
162
|
+
}
|
|
163
|
+
__name(readAuthTrace, "readAuthTrace");
|
|
164
|
+
function clearAuthTrace() {
|
|
165
|
+
memory = [];
|
|
166
|
+
loaded = true;
|
|
167
|
+
try {
|
|
168
|
+
backend()?.removeItem(KEY);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
__name(clearAuthTrace, "clearAuthTrace");
|
|
173
|
+
function context() {
|
|
174
|
+
if (!hasWindow) return [];
|
|
175
|
+
const copies = globalThis.__cfgAuthCopies ?? 1;
|
|
176
|
+
const lines = [`url: ${window.location.href.split("?")[0]}`, `copies: ${String(copies)}`, `online: ${navigator.onLine}`, `ua: ${navigator.userAgent}`];
|
|
177
|
+
try {
|
|
178
|
+
const auth2 = globalThis.__cfgAuthNow;
|
|
179
|
+
const now = auth2?.();
|
|
180
|
+
if (now) lines.push(`now: ${Object.entries(now).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}`);
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
185
|
+
__name(context, "context");
|
|
186
|
+
function dumpAuthTrace() {
|
|
187
|
+
const entries = load();
|
|
188
|
+
const first = entries[0]?.t ?? Date.now();
|
|
189
|
+
const rows = entries.map((e) => {
|
|
190
|
+
const fields = e.f ? ` ${Object.entries(e.f).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}` : "";
|
|
191
|
+
const at = new Date(e.t).toISOString().slice(11, 23);
|
|
192
|
+
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}` : ""}`;
|
|
193
|
+
});
|
|
194
|
+
return [`# cfg auth trace, ${entries.length} events`, ...context(), "", ...rows].join("\n");
|
|
195
|
+
}
|
|
196
|
+
__name(dumpAuthTrace, "dumpAuthTrace");
|
|
197
|
+
function installAuthTrace() {
|
|
198
|
+
if (!hasWindow) return;
|
|
199
|
+
const g = globalThis;
|
|
200
|
+
g.__cfgAuthTrace = authTrace;
|
|
201
|
+
g.__cfgAuth = {
|
|
202
|
+
trace: readAuthTrace,
|
|
203
|
+
dump: /* @__PURE__ */ __name(() => {
|
|
204
|
+
const text = dumpAuthTrace();
|
|
205
|
+
console.log(text);
|
|
206
|
+
return text;
|
|
207
|
+
}, "dump"),
|
|
208
|
+
clear: clearAuthTrace,
|
|
209
|
+
/** Put the dump on the clipboard (works when the console output is blocked or too long to select). */
|
|
210
|
+
copy: /* @__PURE__ */ __name(() => {
|
|
211
|
+
const text = dumpAuthTrace();
|
|
212
|
+
void navigator.clipboard?.writeText(text);
|
|
213
|
+
return `${text.split("\n").length} lines copied`;
|
|
214
|
+
}, "copy"),
|
|
215
|
+
/** Download the raw trace as a JSON file. */
|
|
216
|
+
download: /* @__PURE__ */ __name(() => {
|
|
217
|
+
const blob = new Blob([JSON.stringify(readAuthTrace(), null, 1)], { type: "application/json" });
|
|
218
|
+
const a = document.createElement("a");
|
|
219
|
+
a.href = URL.createObjectURL(blob);
|
|
220
|
+
a.download = `auth-trace-${Date.now()}.json`;
|
|
221
|
+
a.click();
|
|
222
|
+
}, "download")
|
|
223
|
+
};
|
|
224
|
+
const queued = g.__cfgAuthTraceQ;
|
|
225
|
+
if (Array.isArray(queued)) {
|
|
226
|
+
g.__cfgAuthTraceQ = [];
|
|
227
|
+
for (const item of queued) {
|
|
228
|
+
if (Array.isArray(item) && typeof item[1] === "string") {
|
|
229
|
+
try {
|
|
230
|
+
record(Number(item[0]) || Date.now(), item[1], item[2]);
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
__name(installAuthTrace, "installAuthTrace");
|
|
238
|
+
function registerCopy() {
|
|
239
|
+
if (!hasWindow) return;
|
|
240
|
+
const g = globalThis;
|
|
241
|
+
const n = (g.__cfgAuthCopies ?? 0) + 1;
|
|
242
|
+
g.__cfgAuthCopies = n;
|
|
243
|
+
if (n > 1) {
|
|
244
|
+
authTrace("dup.copy", { copies: n });
|
|
245
|
+
if (n === 2) {
|
|
246
|
+
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.");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
__name(registerCopy, "registerCopy");
|
|
251
|
+
registerCopy();
|
|
252
|
+
installAuthTrace();
|
|
253
|
+
|
|
67
254
|
// src/_api/generated/helpers/errors.ts
|
|
68
255
|
var APIError = class extends Error {
|
|
69
256
|
constructor(statusCode, statusText, response, url, message) {
|
|
@@ -180,10 +367,10 @@ function normalizeSegment(raw) {
|
|
|
180
367
|
return raw.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
|
|
181
368
|
}
|
|
182
369
|
__name(normalizeSegment, "normalizeSegment");
|
|
183
|
-
var
|
|
370
|
+
var hasWindow2 = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
|
|
184
371
|
var domainByHost = /* @__PURE__ */ new Map();
|
|
185
372
|
function detectCookieDomain() {
|
|
186
|
-
if (!
|
|
373
|
+
if (!hasWindow2() || typeof document === "undefined") return null;
|
|
187
374
|
const host = window.location.hostname.toLowerCase();
|
|
188
375
|
if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
|
|
189
376
|
const cached = domainByHost.get(host);
|
|
@@ -207,7 +394,7 @@ function detectCookieDomain() {
|
|
|
207
394
|
}
|
|
208
395
|
__name(detectCookieDomain, "detectCookieDomain");
|
|
209
396
|
function nativeStorage(store) {
|
|
210
|
-
if (!
|
|
397
|
+
if (!hasWindow2()) return null;
|
|
211
398
|
try {
|
|
212
399
|
return store === "session" ? window.sessionStorage : window.localStorage;
|
|
213
400
|
} catch {
|
|
@@ -216,7 +403,7 @@ function nativeStorage(store) {
|
|
|
216
403
|
}
|
|
217
404
|
__name(nativeStorage, "nativeStorage");
|
|
218
405
|
function cookieNames() {
|
|
219
|
-
if (!
|
|
406
|
+
if (!hasWindow2()) return [];
|
|
220
407
|
try {
|
|
221
408
|
return document.cookie.split("; ").map((part) => decodeURIComponent(part.split("=")[0] ?? "")).filter(Boolean);
|
|
222
409
|
} catch {
|
|
@@ -241,7 +428,7 @@ function createStorage(owner, options = {}) {
|
|
|
241
428
|
const families = Object.values(options.families ?? {});
|
|
242
429
|
const specByKey = /* @__PURE__ */ new Map();
|
|
243
430
|
for (const spec of specs) specByKey.set(spec.key, [...specByKey.get(spec.key) ?? [], spec]);
|
|
244
|
-
const
|
|
431
|
+
const memory2 = {
|
|
245
432
|
local: /* @__PURE__ */ new Map(),
|
|
246
433
|
session: /* @__PURE__ */ new Map(),
|
|
247
434
|
cookie: /* @__PURE__ */ new Map()
|
|
@@ -255,7 +442,7 @@ function createStorage(owner, options = {}) {
|
|
|
255
442
|
if (!key.startsWith(prefix)) throw new TypeError(`storage key "${key}" is not owned by "${owner}"`);
|
|
256
443
|
}, "assertOwned");
|
|
257
444
|
function rawGet(store, key) {
|
|
258
|
-
if (!
|
|
445
|
+
if (!hasWindow2()) return null;
|
|
259
446
|
if (store === "cookie") {
|
|
260
447
|
try {
|
|
261
448
|
const escaped = encodeURIComponent(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -263,18 +450,18 @@ function createStorage(owner, options = {}) {
|
|
|
263
450
|
if (match?.[1] !== void 0) return decodeURIComponent(match[1]);
|
|
264
451
|
} catch {
|
|
265
452
|
}
|
|
266
|
-
return
|
|
453
|
+
return memory2.cookie.get(key) ?? null;
|
|
267
454
|
}
|
|
268
455
|
try {
|
|
269
456
|
const value = nativeStorage(store)?.getItem(key) ?? null;
|
|
270
457
|
if (value !== null) return value;
|
|
271
458
|
} catch {
|
|
272
459
|
}
|
|
273
|
-
return
|
|
460
|
+
return memory2[store].get(key) ?? null;
|
|
274
461
|
}
|
|
275
462
|
__name(rawGet, "rawGet");
|
|
276
463
|
function rawSet(store, key, value, maxAgeSeconds) {
|
|
277
|
-
if (!
|
|
464
|
+
if (!hasWindow2()) return false;
|
|
278
465
|
try {
|
|
279
466
|
if (store === "cookie") {
|
|
280
467
|
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
@@ -282,24 +469,24 @@ function createStorage(owner, options = {}) {
|
|
|
282
469
|
const domainPart = domain ? `; Domain=${domain}` : "";
|
|
283
470
|
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
|
|
284
471
|
if (domain) expireCookie(key, null);
|
|
285
|
-
if (rawGet("cookie", key) === value && !
|
|
286
|
-
|
|
472
|
+
if (rawGet("cookie", key) === value && !memory2.cookie.has(key)) return true;
|
|
473
|
+
memory2.cookie.set(key, value);
|
|
287
474
|
return false;
|
|
288
475
|
}
|
|
289
476
|
const native = nativeStorage(store);
|
|
290
477
|
if (!native) throw new Error("storage unavailable");
|
|
291
478
|
native.setItem(key, value);
|
|
292
|
-
|
|
479
|
+
memory2[store].delete(key);
|
|
293
480
|
return true;
|
|
294
481
|
} catch {
|
|
295
|
-
|
|
482
|
+
memory2[store].set(key, value);
|
|
296
483
|
return false;
|
|
297
484
|
}
|
|
298
485
|
}
|
|
299
486
|
__name(rawSet, "rawSet");
|
|
300
487
|
function rawRemove(store, key) {
|
|
301
|
-
|
|
302
|
-
if (!
|
|
488
|
+
memory2[store].delete(key);
|
|
489
|
+
if (!hasWindow2()) return;
|
|
303
490
|
try {
|
|
304
491
|
if (store === "cookie") {
|
|
305
492
|
expireCookie(key, null);
|
|
@@ -328,7 +515,7 @@ function createStorage(owner, options = {}) {
|
|
|
328
515
|
}
|
|
329
516
|
} catch {
|
|
330
517
|
}
|
|
331
|
-
for (const name of
|
|
518
|
+
for (const name of memory2[store].keys()) if (name.startsWith(startsWith) && !out.includes(name)) out.push(name);
|
|
332
519
|
return out;
|
|
333
520
|
}
|
|
334
521
|
__name(listKeys, "listKeys");
|
|
@@ -400,7 +587,7 @@ function createStorage(owner, options = {}) {
|
|
|
400
587
|
cookieDomain: resolveCookieDomain,
|
|
401
588
|
onChange(key, listener, store = "local") {
|
|
402
589
|
assertValid(key);
|
|
403
|
-
if (!
|
|
590
|
+
if (!hasWindow2()) return () => {
|
|
404
591
|
};
|
|
405
592
|
const handler = /* @__PURE__ */ __name((event) => {
|
|
406
593
|
if (event.storageArea !== nativeStorage(store)) return;
|
|
@@ -424,6 +611,29 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
|
|
|
424
611
|
var API_KEY_KEY = "cfg.auth.api_key";
|
|
425
612
|
var storage = true ? cfgStorage : createStorage2("cfg");
|
|
426
613
|
var isBrowser = typeof window !== "undefined";
|
|
614
|
+
function trace(ev, fields) {
|
|
615
|
+
try {
|
|
616
|
+
const g = globalThis;
|
|
617
|
+
const sink = g.__cfgAuthTrace;
|
|
618
|
+
if (sink) {
|
|
619
|
+
sink(ev, fields);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const queue = g.__cfgAuthTraceQ ?? [];
|
|
623
|
+
g.__cfgAuthTraceQ = queue;
|
|
624
|
+
if (queue.length < 100) queue.push([Date.now(), ev, fields]);
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
__name(trace, "trace");
|
|
629
|
+
function pathOf(url) {
|
|
630
|
+
try {
|
|
631
|
+
return new URL(url, "http://x").pathname;
|
|
632
|
+
} catch {
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
__name(pathOf, "pathOf");
|
|
427
637
|
function secondsUntilExp(token) {
|
|
428
638
|
const exp = jwtExpMs(token);
|
|
429
639
|
return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
|
|
@@ -454,10 +664,10 @@ function adoptLegacySession() {
|
|
|
454
664
|
if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
|
|
455
665
|
const legacy = backendFor("localStorage");
|
|
456
666
|
const access = legacy.get(ACCESS_KEY);
|
|
457
|
-
const
|
|
458
|
-
if (access === null &&
|
|
667
|
+
const refresh2 = legacy.get(REFRESH_KEY);
|
|
668
|
+
if (access === null && refresh2 === null) return;
|
|
459
669
|
if (access !== null) _storage.set(ACCESS_KEY, access);
|
|
460
|
-
if (
|
|
670
|
+
if (refresh2 !== null) _storage.set(REFRESH_KEY, refresh2);
|
|
461
671
|
legacy.set(ACCESS_KEY, null);
|
|
462
672
|
legacy.set(REFRESH_KEY, null);
|
|
463
673
|
} catch {
|
|
@@ -542,12 +752,12 @@ function jwtExpMs(token) {
|
|
|
542
752
|
__name(jwtExpMs, "jwtExpMs");
|
|
543
753
|
function computeSnapshot() {
|
|
544
754
|
const access = _storage.get(ACCESS_KEY);
|
|
545
|
-
const
|
|
755
|
+
const refresh2 = _storage.get(REFRESH_KEY);
|
|
546
756
|
const now = Date.now();
|
|
547
757
|
const accessExp = access ? jwtExpMs(access) : null;
|
|
548
758
|
const accessAlive = access !== null && (accessExp === null || accessExp > now);
|
|
549
|
-
const refreshExp =
|
|
550
|
-
const refreshAlive =
|
|
759
|
+
const refreshExp = refresh2 ? jwtExpMs(refresh2) : null;
|
|
760
|
+
const refreshAlive = refresh2 !== null && (refreshExp === null || refreshExp > now);
|
|
551
761
|
return {
|
|
552
762
|
status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
|
|
553
763
|
accessExpiresAt: accessExp
|
|
@@ -570,9 +780,9 @@ function scheduleExpiryFlip() {
|
|
|
570
780
|
const now = Date.now();
|
|
571
781
|
const exps = [];
|
|
572
782
|
const access = _storage.get(ACCESS_KEY);
|
|
573
|
-
const
|
|
783
|
+
const refresh2 = _storage.get(REFRESH_KEY);
|
|
574
784
|
const accessExp = access ? jwtExpMs(access) : null;
|
|
575
|
-
const refreshExp =
|
|
785
|
+
const refreshExp = refresh2 ? jwtExpMs(refresh2) : null;
|
|
576
786
|
if (accessExp !== null && accessExp > now) exps.push(accessExp);
|
|
577
787
|
if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
|
|
578
788
|
if (!exps.length) return;
|
|
@@ -583,7 +793,10 @@ __name(scheduleExpiryFlip, "scheduleExpiryFlip");
|
|
|
583
793
|
function notifySessionChanged() {
|
|
584
794
|
const next = computeSnapshot();
|
|
585
795
|
const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
|
|
586
|
-
if (changed)
|
|
796
|
+
if (changed) {
|
|
797
|
+
_snapshot = next;
|
|
798
|
+
trace("store.snapshot", { status: next.status });
|
|
799
|
+
}
|
|
587
800
|
scheduleExpiryFlip();
|
|
588
801
|
if (!changed) return;
|
|
589
802
|
for (const listener of Array.from(_sessionListeners)) {
|
|
@@ -666,6 +879,7 @@ var auth = {
|
|
|
666
879
|
return _storage.get(ACCESS_KEY);
|
|
667
880
|
},
|
|
668
881
|
setToken(token) {
|
|
882
|
+
trace("store.setToken", { present: token !== null });
|
|
669
883
|
_storage.set(ACCESS_KEY, token);
|
|
670
884
|
notifySessionChanged();
|
|
671
885
|
},
|
|
@@ -673,14 +887,16 @@ var auth = {
|
|
|
673
887
|
return _storage.get(REFRESH_KEY);
|
|
674
888
|
},
|
|
675
889
|
setRefreshToken(token) {
|
|
890
|
+
trace("store.setRefreshToken", { present: token !== null });
|
|
676
891
|
_storage.set(REFRESH_KEY, token);
|
|
677
892
|
notifySessionChanged();
|
|
678
893
|
},
|
|
679
894
|
clearTokens() {
|
|
895
|
+
trace("store.clearTokens");
|
|
680
896
|
for (const mode of ["localStorage", "sessionStorage", "cookie"]) {
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
897
|
+
const backend2 = backendFor(mode);
|
|
898
|
+
backend2.set(ACCESS_KEY, null);
|
|
899
|
+
backend2.set(REFRESH_KEY, null);
|
|
684
900
|
}
|
|
685
901
|
storage.clearAll();
|
|
686
902
|
notifySessionChanged();
|
|
@@ -699,6 +915,7 @@ var auth = {
|
|
|
699
915
|
* rotation); `refresh: null` explicitly drops it.
|
|
700
916
|
*/
|
|
701
917
|
setSession(tokens) {
|
|
918
|
+
trace("store.setSession", { refresh: tokens.refresh === void 0 ? "kept" : tokens.refresh === null ? "dropped" : "set" });
|
|
702
919
|
_storage.set(ACCESS_KEY, tokens.access);
|
|
703
920
|
if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
|
|
704
921
|
notifySessionChanged();
|
|
@@ -834,10 +1051,14 @@ async function tryRefresh() {
|
|
|
834
1051
|
if (_refreshInflight) return _refreshInflight;
|
|
835
1052
|
if (!_refreshHandler) return null;
|
|
836
1053
|
const runRefresh = /* @__PURE__ */ __name(async () => {
|
|
837
|
-
const
|
|
838
|
-
if (!
|
|
839
|
-
const result = await _refreshHandler(
|
|
840
|
-
if (!result?.access)
|
|
1054
|
+
const refresh2 = auth.getRefreshToken();
|
|
1055
|
+
if (!refresh2) return null;
|
|
1056
|
+
const result = await _refreshHandler(refresh2);
|
|
1057
|
+
if (!result?.access) {
|
|
1058
|
+
trace("refresh", { ok: false, why: "no-access" });
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
trace("refresh", { ok: true, rotated: Boolean(result.refresh) });
|
|
841
1062
|
auth.setToken(result.access);
|
|
842
1063
|
if (result.refresh) auth.setRefreshToken(result.refresh);
|
|
843
1064
|
return result.access;
|
|
@@ -850,6 +1071,7 @@ async function tryRefresh() {
|
|
|
850
1071
|
}
|
|
851
1072
|
return await runRefresh();
|
|
852
1073
|
} catch {
|
|
1074
|
+
trace("refresh", { ok: false, why: "threw" });
|
|
853
1075
|
return null;
|
|
854
1076
|
} finally {
|
|
855
1077
|
_refreshInflight = null;
|
|
@@ -858,7 +1080,8 @@ async function tryRefresh() {
|
|
|
858
1080
|
return _refreshInflight;
|
|
859
1081
|
}
|
|
860
1082
|
__name(tryRefresh, "tryRefresh");
|
|
861
|
-
function expireSession(response) {
|
|
1083
|
+
function expireSession(response, why) {
|
|
1084
|
+
trace("store.expire", { why, status: response.status, path: pathOf(response.url), handlers: _sessionExpiredHandlers.size });
|
|
862
1085
|
auth.clearTokens();
|
|
863
1086
|
for (const cb of Array.from(_sessionExpiredHandlers)) {
|
|
864
1087
|
try {
|
|
@@ -1001,15 +1224,16 @@ function installAuthOnClient(client2) {
|
|
|
1001
1224
|
client2.interceptors.response.use(async (response, request) => {
|
|
1002
1225
|
if (response.status !== 401) return response;
|
|
1003
1226
|
if (/\/accounts\/(otp|oauth|login|register|token)\b/.test(request.url)) {
|
|
1227
|
+
trace("401.passthrough", { path: pathOf(request.url) });
|
|
1004
1228
|
return response;
|
|
1005
1229
|
}
|
|
1006
1230
|
if (request.headers.get(RETRY_MARKER)) {
|
|
1007
|
-
expireSession(response);
|
|
1231
|
+
expireSession(response, "retry-marker");
|
|
1008
1232
|
return response;
|
|
1009
1233
|
}
|
|
1010
1234
|
const newToken = await tryRefresh();
|
|
1011
1235
|
if (!newToken) {
|
|
1012
|
-
expireSession(response);
|
|
1236
|
+
expireSession(response, "refresh-failed");
|
|
1013
1237
|
return response;
|
|
1014
1238
|
}
|
|
1015
1239
|
const retry = request.clone();
|
|
@@ -1021,7 +1245,7 @@ function installAuthOnClient(client2) {
|
|
|
1021
1245
|
}
|
|
1022
1246
|
try {
|
|
1023
1247
|
const retried = await fetch(retry);
|
|
1024
|
-
if (retried.status === 401) expireSession(retried);
|
|
1248
|
+
if (retried.status === 401) expireSession(retried, "retry-401");
|
|
1025
1249
|
return retried;
|
|
1026
1250
|
} catch {
|
|
1027
1251
|
return response;
|
|
@@ -1030,7 +1254,7 @@ function installAuthOnClient(client2) {
|
|
|
1030
1254
|
}
|
|
1031
1255
|
__name(installAuthOnClient, "installAuthOnClient");
|
|
1032
1256
|
var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
|
|
1033
|
-
auth.setRefreshHandler(async (
|
|
1257
|
+
auth.setRefreshHandler(async (refresh2) => {
|
|
1034
1258
|
try {
|
|
1035
1259
|
const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
|
|
1036
1260
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -1042,11 +1266,11 @@ auth.setRefreshHandler(async (refresh) => {
|
|
|
1042
1266
|
method: "POST",
|
|
1043
1267
|
headers,
|
|
1044
1268
|
credentials: auth.getWithCredentials() ? "include" : "same-origin",
|
|
1045
|
-
body: JSON.stringify({ refresh })
|
|
1269
|
+
body: JSON.stringify({ refresh: refresh2 })
|
|
1046
1270
|
});
|
|
1047
1271
|
if (!res.ok) return null;
|
|
1048
1272
|
const data = await res.json();
|
|
1049
|
-
return data?.access ? { access: data.access, refresh: data.refresh ??
|
|
1273
|
+
return data?.access ? { access: data.access, refresh: data.refresh ?? refresh2 } : null;
|
|
1050
1274
|
} catch {
|
|
1051
1275
|
return null;
|
|
1052
1276
|
}
|