@djangocfg/api 2.1.603 → 2.1.604
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 +94 -13
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.mjs +94 -13
- package/dist/auth.mjs.map +1 -1
- package/dist/clients.cjs +94 -13
- package/dist/clients.cjs.map +1 -1
- package/dist/clients.mjs +94 -13
- package/dist/clients.mjs.map +1 -1
- package/dist/hooks.cjs +94 -13
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.mjs +94 -13
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.cjs +94 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.mjs +94 -13
- package/dist/index.mjs.map +1 -1
- package/dist/storage.cjs +67 -9
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.d.cts +24 -2
- package/dist/storage.d.ts +24 -2
- package/dist/storage.mjs +67 -9
- package/dist/storage.mjs.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/sharedCookie.test.ts +112 -0
- package/src/_api/generated/helpers/auth.ts +58 -9
- package/src/_api/generated/helpers/keys.ts +74 -9
- package/src/cookies.ts +22 -0
- package/src/storage.ts +2 -0
package/dist/index.d.cts
CHANGED
|
@@ -61,6 +61,11 @@ declare const auth: {
|
|
|
61
61
|
* the mode is module state and resets on reload, so a later switch strands
|
|
62
62
|
* whatever was written after it. */
|
|
63
63
|
setStorageMode(mode: StorageMode): void;
|
|
64
|
+
/** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
|
|
65
|
+
* cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
|
|
66
|
+
setCookieDomain(domain: "auto" | string | null): void;
|
|
67
|
+
/** The `Domain` the session cookie is written with; `null` = host-only. */
|
|
68
|
+
getCookieDomain(): string | null;
|
|
64
69
|
getToken(): string | null;
|
|
65
70
|
setToken(token: string | null): void;
|
|
66
71
|
getRefreshToken(): string | null;
|
|
@@ -2601,6 +2606,16 @@ interface StorageOptions {
|
|
|
2601
2606
|
readonly families?: Readonly<Record<string, FamilySpec>>;
|
|
2602
2607
|
/** Cookie lifetime for the `cookie` store. Default 30 days. */
|
|
2603
2608
|
readonly cookieMaxAgeSeconds?: number;
|
|
2609
|
+
/**
|
|
2610
|
+
* Cookie `Domain`. `'auto'` (default): the registrable domain of the page, so every subdomain
|
|
2611
|
+
* shares the cookie (`example.com` for `my.example.com`). A string forces a domain; `false`
|
|
2612
|
+
* keeps cookies host-only.
|
|
2613
|
+
*/
|
|
2614
|
+
readonly cookieDomain?: 'auto' | string | false;
|
|
2615
|
+
}
|
|
2616
|
+
interface WriteOptions {
|
|
2617
|
+
/** Cookie store: lifetime of this write. Default: the owner's `cookieMaxAgeSeconds`. */
|
|
2618
|
+
readonly maxAgeSeconds?: number;
|
|
2604
2619
|
}
|
|
2605
2620
|
interface GuardedStorage {
|
|
2606
2621
|
readonly owner: string;
|
|
@@ -2612,7 +2627,7 @@ interface GuardedStorage {
|
|
|
2612
2627
|
isValid(key: string): boolean;
|
|
2613
2628
|
read(key: string, store?: KeyStore): string | null;
|
|
2614
2629
|
/** True when the value reached the browser's storage; false means it is held in memory for this page only. */
|
|
2615
|
-
write(key: string, value: string, store?: KeyStore): boolean;
|
|
2630
|
+
write(key: string, value: string, store?: KeyStore, options?: WriteOptions): boolean;
|
|
2616
2631
|
/** Removes the key. */
|
|
2617
2632
|
remove(key: string, store?: KeyStore): void;
|
|
2618
2633
|
readJSON<T>(key: string, store?: KeyStore): T | null;
|
|
@@ -2623,6 +2638,10 @@ interface GuardedStorage {
|
|
|
2623
2638
|
clearAll(options?: {
|
|
2624
2639
|
all?: boolean;
|
|
2625
2640
|
}): void;
|
|
2641
|
+
/** Cookie store: change the `Domain` policy (`'auto'`, a domain, or `false` for host-only cookies). */
|
|
2642
|
+
setCookieDomain(domain: 'auto' | string | false): void;
|
|
2643
|
+
/** The `Domain` cookies are written with right now; `null` means host-only. */
|
|
2644
|
+
cookieDomain(): string | null;
|
|
2626
2645
|
/** Cross-tab: calls `listener` with the new value when ANOTHER tab changes `key`. Cookies fire no events. */
|
|
2627
2646
|
onChange(key: string, listener: (value: string | null) => void, store?: Exclude<KeyStore, 'cookie'>): () => void;
|
|
2628
2647
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -61,6 +61,11 @@ declare const auth: {
|
|
|
61
61
|
* the mode is module state and resets on reload, so a later switch strands
|
|
62
62
|
* whatever was written after it. */
|
|
63
63
|
setStorageMode(mode: StorageMode): void;
|
|
64
|
+
/** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
|
|
65
|
+
* cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
|
|
66
|
+
setCookieDomain(domain: "auto" | string | null): void;
|
|
67
|
+
/** The `Domain` the session cookie is written with; `null` = host-only. */
|
|
68
|
+
getCookieDomain(): string | null;
|
|
64
69
|
getToken(): string | null;
|
|
65
70
|
setToken(token: string | null): void;
|
|
66
71
|
getRefreshToken(): string | null;
|
|
@@ -2601,6 +2606,16 @@ interface StorageOptions {
|
|
|
2601
2606
|
readonly families?: Readonly<Record<string, FamilySpec>>;
|
|
2602
2607
|
/** Cookie lifetime for the `cookie` store. Default 30 days. */
|
|
2603
2608
|
readonly cookieMaxAgeSeconds?: number;
|
|
2609
|
+
/**
|
|
2610
|
+
* Cookie `Domain`. `'auto'` (default): the registrable domain of the page, so every subdomain
|
|
2611
|
+
* shares the cookie (`example.com` for `my.example.com`). A string forces a domain; `false`
|
|
2612
|
+
* keeps cookies host-only.
|
|
2613
|
+
*/
|
|
2614
|
+
readonly cookieDomain?: 'auto' | string | false;
|
|
2615
|
+
}
|
|
2616
|
+
interface WriteOptions {
|
|
2617
|
+
/** Cookie store: lifetime of this write. Default: the owner's `cookieMaxAgeSeconds`. */
|
|
2618
|
+
readonly maxAgeSeconds?: number;
|
|
2604
2619
|
}
|
|
2605
2620
|
interface GuardedStorage {
|
|
2606
2621
|
readonly owner: string;
|
|
@@ -2612,7 +2627,7 @@ interface GuardedStorage {
|
|
|
2612
2627
|
isValid(key: string): boolean;
|
|
2613
2628
|
read(key: string, store?: KeyStore): string | null;
|
|
2614
2629
|
/** True when the value reached the browser's storage; false means it is held in memory for this page only. */
|
|
2615
|
-
write(key: string, value: string, store?: KeyStore): boolean;
|
|
2630
|
+
write(key: string, value: string, store?: KeyStore, options?: WriteOptions): boolean;
|
|
2616
2631
|
/** Removes the key. */
|
|
2617
2632
|
remove(key: string, store?: KeyStore): void;
|
|
2618
2633
|
readJSON<T>(key: string, store?: KeyStore): T | null;
|
|
@@ -2623,6 +2638,10 @@ interface GuardedStorage {
|
|
|
2623
2638
|
clearAll(options?: {
|
|
2624
2639
|
all?: boolean;
|
|
2625
2640
|
}): void;
|
|
2641
|
+
/** Cookie store: change the `Domain` policy (`'auto'`, a domain, or `false` for host-only cookies). */
|
|
2642
|
+
setCookieDomain(domain: 'auto' | string | false): void;
|
|
2643
|
+
/** The `Domain` cookies are written with right now; `null` means host-only. */
|
|
2644
|
+
cookieDomain(): string | null;
|
|
2626
2645
|
/** Cross-tab: calls `listener` with the new value when ANOTHER tab changes `key`. Cookies fire no events. */
|
|
2627
2646
|
onChange(key: string, listener: (value: string | null) => void, store?: Exclude<KeyStore, 'cookie'>): () => void;
|
|
2628
2647
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -82,8 +82,8 @@ var NetworkError = class extends Error {
|
|
|
82
82
|
var k = /* @__PURE__ */ __name((key, store, personal) => ({ key, store, personal }), "k");
|
|
83
83
|
var f = /* @__PURE__ */ __name((prefix, store, personal) => ({ prefix, store, personal }), "f");
|
|
84
84
|
var CFG_KEYS = {
|
|
85
|
-
accessToken: k("cfg.auth.access_token", "
|
|
86
|
-
refreshToken: k("cfg.auth.refresh_token", "
|
|
85
|
+
accessToken: k("cfg.auth.access_token", "cookie", true),
|
|
86
|
+
refreshToken: k("cfg.auth.refresh_token", "cookie", true),
|
|
87
87
|
apiKey: k("cfg.auth.api_key", "local", true),
|
|
88
88
|
email: k("cfg.auth.email", "local", false),
|
|
89
89
|
emailTs: k("cfg.auth.email_ts", "local", false),
|
|
@@ -118,6 +118,31 @@ function normalizeSegment(raw) {
|
|
|
118
118
|
}
|
|
119
119
|
__name(normalizeSegment, "normalizeSegment");
|
|
120
120
|
var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
|
|
121
|
+
var domainByHost = /* @__PURE__ */ new Map();
|
|
122
|
+
function detectCookieDomain() {
|
|
123
|
+
if (!hasWindow() || typeof document === "undefined") return null;
|
|
124
|
+
const host = window.location.hostname.toLowerCase();
|
|
125
|
+
if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
|
|
126
|
+
const cached = domainByHost.get(host);
|
|
127
|
+
if (cached !== void 0) return cached;
|
|
128
|
+
const labels = host.split(".");
|
|
129
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
130
|
+
let found = null;
|
|
131
|
+
try {
|
|
132
|
+
for (let i = labels.length - 2; i >= 0 && found === null; i -= 1) {
|
|
133
|
+
const candidate = labels.slice(i).join(".");
|
|
134
|
+
const probe = "cfg_probe_" + Math.random().toString(36).slice(2);
|
|
135
|
+
document.cookie = probe + "=1; Domain=" + candidate + "; Path=/; Max-Age=30; SameSite=Lax" + secure;
|
|
136
|
+
if (document.cookie.includes(probe + "=1")) found = candidate;
|
|
137
|
+
document.cookie = probe + "=; Domain=" + candidate + "; Path=/; Max-Age=0; SameSite=Lax" + secure;
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
found = null;
|
|
141
|
+
}
|
|
142
|
+
domainByHost.set(host, found);
|
|
143
|
+
return found;
|
|
144
|
+
}
|
|
145
|
+
__name(detectCookieDomain, "detectCookieDomain");
|
|
121
146
|
function nativeStorage(store) {
|
|
122
147
|
if (!hasWindow()) return null;
|
|
123
148
|
try {
|
|
@@ -143,6 +168,12 @@ function createStorage(owner, options = {}) {
|
|
|
143
168
|
const prefix = `${owner}.`;
|
|
144
169
|
const pattern = owner === "cfg" ? /^cfg(\.[a-z0-9_]+)+$/ : new RegExp(`^(cfg|${owner})(\\.[a-z0-9_]+)+$`);
|
|
145
170
|
const cookieMaxAge = options.cookieMaxAgeSeconds ?? 60 * 60 * 24 * 30;
|
|
171
|
+
let cookiePolicy = options.cookieDomain ?? "auto";
|
|
172
|
+
const resolveCookieDomain = /* @__PURE__ */ __name(() => cookiePolicy === false ? null : cookiePolicy === "auto" ? detectCookieDomain() : cookiePolicy, "resolveCookieDomain");
|
|
173
|
+
const expireCookie = /* @__PURE__ */ __name((key, domain) => {
|
|
174
|
+
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
175
|
+
document.cookie = encodeURIComponent(key) + "=; Path=/; Max-Age=0; SameSite=Lax" + (domain ? "; Domain=" + domain : "") + secure;
|
|
176
|
+
}, "expireCookie");
|
|
146
177
|
const specs = Object.values(options.keys ?? {});
|
|
147
178
|
const families = Object.values(options.families ?? {});
|
|
148
179
|
const specByKey = /* @__PURE__ */ new Map();
|
|
@@ -179,12 +210,15 @@ function createStorage(owner, options = {}) {
|
|
|
179
210
|
return memory[store].get(key) ?? null;
|
|
180
211
|
}
|
|
181
212
|
__name(rawGet, "rawGet");
|
|
182
|
-
function rawSet(store, key, value) {
|
|
213
|
+
function rawSet(store, key, value, maxAgeSeconds) {
|
|
183
214
|
if (!hasWindow()) return false;
|
|
184
215
|
try {
|
|
185
216
|
if (store === "cookie") {
|
|
186
217
|
const secure = window.location.protocol === "https:" ? "; Secure" : "";
|
|
187
|
-
|
|
218
|
+
const domain = resolveCookieDomain();
|
|
219
|
+
const domainPart = domain ? `; Domain=${domain}` : "";
|
|
220
|
+
document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
|
|
221
|
+
if (domain) expireCookie(key, null);
|
|
188
222
|
if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
|
|
189
223
|
memory.cookie.set(key, value);
|
|
190
224
|
return false;
|
|
@@ -205,8 +239,9 @@ function createStorage(owner, options = {}) {
|
|
|
205
239
|
if (!hasWindow()) return;
|
|
206
240
|
try {
|
|
207
241
|
if (store === "cookie") {
|
|
208
|
-
|
|
209
|
-
|
|
242
|
+
expireCookie(key, null);
|
|
243
|
+
const domain = resolveCookieDomain();
|
|
244
|
+
if (domain) expireCookie(key, domain);
|
|
210
245
|
} else {
|
|
211
246
|
nativeStorage(store)?.removeItem(key);
|
|
212
247
|
}
|
|
@@ -255,10 +290,10 @@ function createStorage(owner, options = {}) {
|
|
|
255
290
|
const target = resolveStore(key, store);
|
|
256
291
|
return rawGet(target, key);
|
|
257
292
|
},
|
|
258
|
-
write(key, value, store) {
|
|
293
|
+
write(key, value, store, writeOptions) {
|
|
259
294
|
assertOwned(key);
|
|
260
295
|
const target = resolveStore(key, store);
|
|
261
|
-
return rawSet(target, key, value);
|
|
296
|
+
return rawSet(target, key, value, writeOptions?.maxAgeSeconds);
|
|
262
297
|
},
|
|
263
298
|
remove(key, store) {
|
|
264
299
|
assertOwned(key);
|
|
@@ -296,6 +331,10 @@ function createStorage(owner, options = {}) {
|
|
|
296
331
|
for (const name of cookieNames()) if (name.startsWith(prefix)) rawRemove("cookie", name);
|
|
297
332
|
}
|
|
298
333
|
},
|
|
334
|
+
setCookieDomain(domain) {
|
|
335
|
+
cookiePolicy = domain;
|
|
336
|
+
},
|
|
337
|
+
cookieDomain: resolveCookieDomain,
|
|
299
338
|
onChange(key, listener, store = "local") {
|
|
300
339
|
assertValid(key);
|
|
301
340
|
if (!hasWindow()) return () => {
|
|
@@ -322,6 +361,11 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
|
|
|
322
361
|
var API_KEY_KEY = "cfg.auth.api_key";
|
|
323
362
|
var storage = true ? cfgStorage : createStorage2("cfg");
|
|
324
363
|
var isBrowser = typeof window !== "undefined";
|
|
364
|
+
function secondsUntilExp(token) {
|
|
365
|
+
const exp = jwtExpMs(token);
|
|
366
|
+
return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
|
|
367
|
+
}
|
|
368
|
+
__name(secondsUntilExp, "secondsUntilExp");
|
|
325
369
|
var STORE_OF = {
|
|
326
370
|
localStorage: "local",
|
|
327
371
|
sessionStorage: "session",
|
|
@@ -333,13 +377,31 @@ function backendFor(mode) {
|
|
|
333
377
|
get: /* @__PURE__ */ __name((key) => storage.read(key, store), "get"),
|
|
334
378
|
set(key, value) {
|
|
335
379
|
if (value === null) storage.remove(key, store);
|
|
336
|
-
else storage.write(key, value, store);
|
|
380
|
+
else storage.write(key, value, store, store === "cookie" && key === REFRESH_KEY ? { maxAgeSeconds: secondsUntilExp(value) } : void 0);
|
|
337
381
|
}
|
|
338
382
|
};
|
|
339
383
|
}
|
|
340
384
|
__name(backendFor, "backendFor");
|
|
341
|
-
var _storageMode = "
|
|
385
|
+
var _storageMode = "cookie";
|
|
342
386
|
var _storage = backendFor(_storageMode);
|
|
387
|
+
var _apiKeyStore = backendFor("localStorage");
|
|
388
|
+
function adoptLegacySession() {
|
|
389
|
+
if (!isBrowser || _storageMode !== "cookie") return;
|
|
390
|
+
try {
|
|
391
|
+
if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
|
|
392
|
+
const legacy = backendFor("localStorage");
|
|
393
|
+
const access = legacy.get(ACCESS_KEY);
|
|
394
|
+
const refresh = legacy.get(REFRESH_KEY);
|
|
395
|
+
if (access === null && refresh === null) return;
|
|
396
|
+
if (access !== null) _storage.set(ACCESS_KEY, access);
|
|
397
|
+
if (refresh !== null) _storage.set(REFRESH_KEY, refresh);
|
|
398
|
+
legacy.set(ACCESS_KEY, null);
|
|
399
|
+
legacy.set(REFRESH_KEY, null);
|
|
400
|
+
} catch {
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
__name(adoptLegacySession, "adoptLegacySession");
|
|
404
|
+
adoptLegacySession();
|
|
343
405
|
function detectLocale() {
|
|
344
406
|
try {
|
|
345
407
|
if (typeof document === "undefined") return null;
|
|
@@ -484,6 +546,14 @@ function ensureStorageSync() {
|
|
|
484
546
|
}
|
|
485
547
|
});
|
|
486
548
|
window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
|
|
549
|
+
window.addEventListener("focus", () => notifySessionChanged());
|
|
550
|
+
document.addEventListener("visibilitychange", () => {
|
|
551
|
+
if (document.visibilityState === "visible") notifySessionChanged();
|
|
552
|
+
});
|
|
553
|
+
try {
|
|
554
|
+
window.cookieStore?.addEventListener("change", () => notifySessionChanged());
|
|
555
|
+
} catch {
|
|
556
|
+
}
|
|
487
557
|
}
|
|
488
558
|
__name(ensureStorageSync, "ensureStorageSync");
|
|
489
559
|
var SESSION_EXPIRED_REGISTRY = `cfg-auth:expired-handlers:${ACCESS_KEY}`;
|
|
@@ -517,6 +587,17 @@ var auth = {
|
|
|
517
587
|
_storage = backendFor(mode);
|
|
518
588
|
notifySessionChanged();
|
|
519
589
|
},
|
|
590
|
+
// ── Shared cookie domain ──────────────────────────────────────────
|
|
591
|
+
/** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
|
|
592
|
+
* cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
|
|
593
|
+
setCookieDomain(domain) {
|
|
594
|
+
storage.setCookieDomain(domain === null ? false : domain);
|
|
595
|
+
notifySessionChanged();
|
|
596
|
+
},
|
|
597
|
+
/** The `Domain` the session cookie is written with; `null` = host-only. */
|
|
598
|
+
getCookieDomain() {
|
|
599
|
+
return storage.cookieDomain();
|
|
600
|
+
},
|
|
520
601
|
// ── Bearer token ──────────────────────────────────────────────────
|
|
521
602
|
getToken() {
|
|
522
603
|
return _storage.get(ACCESS_KEY);
|
|
@@ -603,18 +684,18 @@ var auth = {
|
|
|
603
684
|
},
|
|
604
685
|
// ── API key ───────────────────────────────────────────────────────
|
|
605
686
|
getApiKey() {
|
|
606
|
-
return _apiKeyOverride ??
|
|
687
|
+
return _apiKeyOverride ?? _apiKeyStore.get(API_KEY_KEY) ?? defaultApiKey();
|
|
607
688
|
},
|
|
608
689
|
setApiKey(key) {
|
|
609
690
|
_apiKeyOverride = key;
|
|
610
691
|
},
|
|
611
692
|
setApiKeyPersist(key) {
|
|
612
693
|
_apiKeyOverride = key;
|
|
613
|
-
|
|
694
|
+
_apiKeyStore.set(API_KEY_KEY, key);
|
|
614
695
|
},
|
|
615
696
|
clearApiKey() {
|
|
616
697
|
_apiKeyOverride = null;
|
|
617
|
-
|
|
698
|
+
_apiKeyStore.set(API_KEY_KEY, null);
|
|
618
699
|
},
|
|
619
700
|
// ── Locale ────────────────────────────────────────────────────────
|
|
620
701
|
getLocale() {
|