@djangocfg/api 2.1.603 → 2.1.605

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.mjs CHANGED
@@ -96,8 +96,8 @@ var APIError = class extends Error {
96
96
  var k = /* @__PURE__ */ __name((key, store, personal) => ({ key, store, personal }), "k");
97
97
  var f = /* @__PURE__ */ __name((prefix, store, personal) => ({ prefix, store, personal }), "f");
98
98
  var CFG_KEYS = {
99
- accessToken: k("cfg.auth.access_token", "local", true),
100
- refreshToken: k("cfg.auth.refresh_token", "local", true),
99
+ accessToken: k("cfg.auth.access_token", "cookie", true),
100
+ refreshToken: k("cfg.auth.refresh_token", "cookie", true),
101
101
  apiKey: k("cfg.auth.api_key", "local", true),
102
102
  email: k("cfg.auth.email", "local", false),
103
103
  emailTs: k("cfg.auth.email_ts", "local", false),
@@ -132,6 +132,31 @@ function normalizeSegment(raw) {
132
132
  }
133
133
  __name(normalizeSegment, "normalizeSegment");
134
134
  var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
135
+ var domainByHost = /* @__PURE__ */ new Map();
136
+ function detectCookieDomain() {
137
+ if (!hasWindow() || typeof document === "undefined") return null;
138
+ const host = window.location.hostname.toLowerCase();
139
+ if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
140
+ const cached = domainByHost.get(host);
141
+ if (cached !== void 0) return cached;
142
+ const labels = host.split(".");
143
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
144
+ let found = null;
145
+ try {
146
+ for (let i = labels.length - 2; i >= 0 && found === null; i -= 1) {
147
+ const candidate = labels.slice(i).join(".");
148
+ const probe = "cfg_probe_" + Math.random().toString(36).slice(2);
149
+ document.cookie = probe + "=1; Domain=" + candidate + "; Path=/; Max-Age=30; SameSite=Lax" + secure;
150
+ if (document.cookie.includes(probe + "=1")) found = candidate;
151
+ document.cookie = probe + "=; Domain=" + candidate + "; Path=/; Max-Age=0; SameSite=Lax" + secure;
152
+ }
153
+ } catch {
154
+ found = null;
155
+ }
156
+ domainByHost.set(host, found);
157
+ return found;
158
+ }
159
+ __name(detectCookieDomain, "detectCookieDomain");
135
160
  function nativeStorage(store) {
136
161
  if (!hasWindow()) return null;
137
162
  try {
@@ -157,6 +182,12 @@ function createStorage(owner, options = {}) {
157
182
  const prefix = `${owner}.`;
158
183
  const pattern = owner === "cfg" ? /^cfg(\.[a-z0-9_]+)+$/ : new RegExp(`^(cfg|${owner})(\\.[a-z0-9_]+)+$`);
159
184
  const cookieMaxAge = options.cookieMaxAgeSeconds ?? 60 * 60 * 24 * 30;
185
+ let cookiePolicy = options.cookieDomain ?? "auto";
186
+ const resolveCookieDomain = /* @__PURE__ */ __name(() => cookiePolicy === false ? null : cookiePolicy === "auto" ? detectCookieDomain() : cookiePolicy, "resolveCookieDomain");
187
+ const expireCookie = /* @__PURE__ */ __name((key, domain) => {
188
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
189
+ document.cookie = encodeURIComponent(key) + "=; Path=/; Max-Age=0; SameSite=Lax" + (domain ? "; Domain=" + domain : "") + secure;
190
+ }, "expireCookie");
160
191
  const specs = Object.values(options.keys ?? {});
161
192
  const families = Object.values(options.families ?? {});
162
193
  const specByKey = /* @__PURE__ */ new Map();
@@ -193,12 +224,15 @@ function createStorage(owner, options = {}) {
193
224
  return memory[store].get(key) ?? null;
194
225
  }
195
226
  __name(rawGet, "rawGet");
196
- function rawSet(store, key, value) {
227
+ function rawSet(store, key, value, maxAgeSeconds) {
197
228
  if (!hasWindow()) return false;
198
229
  try {
199
230
  if (store === "cookie") {
200
231
  const secure = window.location.protocol === "https:" ? "; Secure" : "";
201
- document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax${secure}`;
232
+ const domain = resolveCookieDomain();
233
+ const domainPart = domain ? `; Domain=${domain}` : "";
234
+ document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
235
+ if (domain) expireCookie(key, null);
202
236
  if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
203
237
  memory.cookie.set(key, value);
204
238
  return false;
@@ -219,8 +253,9 @@ function createStorage(owner, options = {}) {
219
253
  if (!hasWindow()) return;
220
254
  try {
221
255
  if (store === "cookie") {
222
- const secure = window.location.protocol === "https:" ? "; Secure" : "";
223
- document.cookie = `${encodeURIComponent(key)}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
256
+ expireCookie(key, null);
257
+ const domain = resolveCookieDomain();
258
+ if (domain) expireCookie(key, domain);
224
259
  } else {
225
260
  nativeStorage(store)?.removeItem(key);
226
261
  }
@@ -269,10 +304,10 @@ function createStorage(owner, options = {}) {
269
304
  const target = resolveStore(key, store);
270
305
  return rawGet(target, key);
271
306
  },
272
- write(key, value, store) {
307
+ write(key, value, store, writeOptions) {
273
308
  assertOwned(key);
274
309
  const target = resolveStore(key, store);
275
- return rawSet(target, key, value);
310
+ return rawSet(target, key, value, writeOptions?.maxAgeSeconds);
276
311
  },
277
312
  remove(key, store) {
278
313
  assertOwned(key);
@@ -310,6 +345,10 @@ function createStorage(owner, options = {}) {
310
345
  for (const name of cookieNames()) if (name.startsWith(prefix)) rawRemove("cookie", name);
311
346
  }
312
347
  },
348
+ setCookieDomain(domain) {
349
+ cookiePolicy = domain;
350
+ },
351
+ cookieDomain: resolveCookieDomain,
313
352
  onChange(key, listener, store = "local") {
314
353
  assertValid(key);
315
354
  if (!hasWindow()) return () => {
@@ -336,6 +375,11 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
336
375
  var API_KEY_KEY = "cfg.auth.api_key";
337
376
  var storage = true ? cfgStorage : createStorage2("cfg");
338
377
  var isBrowser = typeof window !== "undefined";
378
+ function secondsUntilExp(token) {
379
+ const exp = jwtExpMs(token);
380
+ return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
381
+ }
382
+ __name(secondsUntilExp, "secondsUntilExp");
339
383
  var STORE_OF = {
340
384
  localStorage: "local",
341
385
  sessionStorage: "session",
@@ -347,13 +391,31 @@ function backendFor(mode) {
347
391
  get: /* @__PURE__ */ __name((key) => storage.read(key, store), "get"),
348
392
  set(key, value) {
349
393
  if (value === null) storage.remove(key, store);
350
- else storage.write(key, value, store);
394
+ else storage.write(key, value, store, store === "cookie" && key === REFRESH_KEY ? { maxAgeSeconds: secondsUntilExp(value) } : void 0);
351
395
  }
352
396
  };
353
397
  }
354
398
  __name(backendFor, "backendFor");
355
- var _storageMode = "localStorage";
399
+ var _storageMode = "cookie";
356
400
  var _storage = backendFor(_storageMode);
401
+ var _apiKeyStore = backendFor("localStorage");
402
+ function adoptLegacySession() {
403
+ if (!isBrowser || _storageMode !== "cookie") return;
404
+ try {
405
+ if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
406
+ const legacy = backendFor("localStorage");
407
+ const access = legacy.get(ACCESS_KEY);
408
+ const refresh = legacy.get(REFRESH_KEY);
409
+ if (access === null && refresh === null) return;
410
+ if (access !== null) _storage.set(ACCESS_KEY, access);
411
+ if (refresh !== null) _storage.set(REFRESH_KEY, refresh);
412
+ legacy.set(ACCESS_KEY, null);
413
+ legacy.set(REFRESH_KEY, null);
414
+ } catch {
415
+ }
416
+ }
417
+ __name(adoptLegacySession, "adoptLegacySession");
418
+ adoptLegacySession();
357
419
  function detectLocale() {
358
420
  try {
359
421
  if (typeof document === "undefined") return null;
@@ -498,6 +560,14 @@ function ensureStorageSync() {
498
560
  }
499
561
  });
500
562
  window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
563
+ window.addEventListener("focus", () => notifySessionChanged());
564
+ document.addEventListener("visibilitychange", () => {
565
+ if (document.visibilityState === "visible") notifySessionChanged();
566
+ });
567
+ try {
568
+ window.cookieStore?.addEventListener("change", () => notifySessionChanged());
569
+ } catch {
570
+ }
501
571
  }
502
572
  __name(ensureStorageSync, "ensureStorageSync");
503
573
  var SESSION_EXPIRED_REGISTRY = `cfg-auth:expired-handlers:${ACCESS_KEY}`;
@@ -531,6 +601,17 @@ var auth = {
531
601
  _storage = backendFor(mode);
532
602
  notifySessionChanged();
533
603
  },
604
+ // ── Shared cookie domain ──────────────────────────────────────────
605
+ /** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
606
+ * cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
607
+ setCookieDomain(domain) {
608
+ storage.setCookieDomain(domain === null ? false : domain);
609
+ notifySessionChanged();
610
+ },
611
+ /** The `Domain` the session cookie is written with; `null` = host-only. */
612
+ getCookieDomain() {
613
+ return storage.cookieDomain();
614
+ },
534
615
  // ── Bearer token ──────────────────────────────────────────────────
535
616
  getToken() {
536
617
  return _storage.get(ACCESS_KEY);
@@ -617,18 +698,18 @@ var auth = {
617
698
  },
618
699
  // ── API key ───────────────────────────────────────────────────────
619
700
  getApiKey() {
620
- return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
701
+ return _apiKeyOverride ?? _apiKeyStore.get(API_KEY_KEY) ?? defaultApiKey();
621
702
  },
622
703
  setApiKey(key) {
623
704
  _apiKeyOverride = key;
624
705
  },
625
706
  setApiKeyPersist(key) {
626
707
  _apiKeyOverride = key;
627
- _storage.set(API_KEY_KEY, key);
708
+ _apiKeyStore.set(API_KEY_KEY, key);
628
709
  },
629
710
  clearApiKey() {
630
711
  _apiKeyOverride = null;
631
- _storage.set(API_KEY_KEY, null);
712
+ _apiKeyStore.set(API_KEY_KEY, null);
632
713
  },
633
714
  // ── Locale ────────────────────────────────────────────────────────
634
715
  getLocale() {
@@ -4299,6 +4380,30 @@ var useDeleteAccount = /* @__PURE__ */ __name(() => {
4299
4380
  };
4300
4381
  }, "useDeleteAccount");
4301
4382
 
4383
+ // src/auth/hooks/legacyStorage.ts
4384
+ var LEGACY_TOKEN_KEYS = [
4385
+ "cfg.auth.access_token",
4386
+ "cfg.auth.refresh_token",
4387
+ "auth_token",
4388
+ "refresh_token",
4389
+ "access_token"
4390
+ ];
4391
+ function purgeLegacyTokenStorage() {
4392
+ const removed = [];
4393
+ try {
4394
+ if (typeof window === "undefined") return removed;
4395
+ for (const key of LEGACY_TOKEN_KEYS) {
4396
+ if (window.localStorage.getItem(key) !== null) {
4397
+ window.localStorage.removeItem(key);
4398
+ removed.push(key);
4399
+ }
4400
+ }
4401
+ } catch {
4402
+ }
4403
+ return removed;
4404
+ }
4405
+ __name(purgeLegacyTokenStorage, "purgeLegacyTokenStorage");
4406
+
4302
4407
  // src/auth/context/AccountsContext.tsx
4303
4408
  import {
4304
4409
  createContext,
@@ -4671,6 +4776,10 @@ __name(hasApiOrigin, "hasApiOrigin");
4671
4776
  var AccountsContext = createContext(void 0);
4672
4777
  function AccountsProvider({ children }) {
4673
4778
  const [profile, setProfile] = useState10(() => {
4779
+ if (!auth.isAuthenticated()) {
4780
+ clearProfileCache();
4781
+ return void 0;
4782
+ }
4674
4783
  const cached = getCachedProfile();
4675
4784
  return cached || void 0;
4676
4785
  });
@@ -4871,6 +4980,10 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
4871
4980
  authLogger.warn("Dead session in storage \u2014 clearing");
4872
4981
  auth.clearSession();
4873
4982
  clearProfileCache();
4983
+ } else if (!hasTokens && !auth.isAuthenticated()) {
4984
+ clearProfileCache();
4985
+ purgeLegacyTokenStorage();
4986
+ if (accountsRef.current.profile) accountsRef.current.logout();
4874
4987
  }
4875
4988
  setProfileSettled(true);
4876
4989
  return;